├── salt ├── cloud │ ├── templates │ │ ├── map.j2 │ │ └── openstack.conf │ └── init.sls ├── mesos-slave │ ├── templates │ │ └── slave │ │ │ ├── port │ │ │ ├── docker │ │ │ ├── docker_remove_delay │ │ │ ├── containerizers │ │ │ ├── hostname │ │ │ ├── executor_registration_timeout │ │ │ ├── ip │ │ │ └── attributes │ └── init.sls ├── mesos-master │ ├── templates │ │ ├── zookeeper │ │ │ ├── myid │ │ │ └── zoo.cfg │ │ └── mesos-master │ │ │ ├── hostname │ │ │ ├── quorum │ │ │ └── ip │ └── init.sls ├── mesos │ ├── templates │ │ ├── zk.j2 │ │ └── cleanup.sh │ ├── map.jinja │ └── init.sls ├── marathon │ ├── templates │ │ ├── conf │ │ │ ├── master │ │ │ └── zk │ │ └── upstart │ └── init.sls ├── mine.sls ├── consul │ ├── templates │ │ ├── template-upstart │ │ ├── dnsmasq │ │ ├── server │ │ │ ├── docker.json │ │ │ └── config.json │ │ ├── upstart │ │ ├── templates │ │ │ ├── nginx-stream.ctmpl │ │ │ ├── nginx.ctmpl │ │ │ └── nginx-web.ctmpl │ │ └── template.hcl │ └── init.sls ├── registry │ ├── init.sls │ └── templates │ │ └── upstart ├── cadvisor │ ├── init.sls │ └── templates │ │ └── upstart ├── schub │ ├── init.sls │ └── templates │ │ └── docker-controller.conf ├── registrator │ ├── init.sls │ └── templates │ │ └── upstart ├── top.sls ├── weave │ ├── templates │ │ ├── upstart │ │ └── scope-upstart │ ├── weave-scope.sls │ └── init.sls ├── nginx │ ├── init.sls │ └── templates │ │ └── nginx.conf └── docker │ ├── templates │ ├── docker_proxy │ ├── default │ └── upstart │ └── init.sls ├── architecture.png ├── monitoring ├── proxy_mysql.json ├── proxy_grafana.json ├── proxy_prometheus.json ├── config_prometheus.yml ├── marathon.json └── dashboard.json ├── pillar ├── cadvisor.sls ├── mine.sls ├── zookeeper.sls ├── top.sls ├── schub.sls ├── mesos.sls ├── weave.sls ├── consul.sls └── docker.sls ├── scaling └── marathon-scaler.json ├── README.md └── LICENSE /salt/cloud/templates/map.j2: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /salt/mesos-slave/templates/slave/port: -------------------------------------------------------------------------------- 1 | 8082 2 | -------------------------------------------------------------------------------- /salt/mesos-slave/templates/slave/docker: -------------------------------------------------------------------------------- 1 | /usr/bin/docker 2 | -------------------------------------------------------------------------------- /salt/mesos-slave/templates/slave/docker_remove_delay: -------------------------------------------------------------------------------- 1 | 15mins 2 | -------------------------------------------------------------------------------- /salt/mesos-master/templates/zookeeper/myid: -------------------------------------------------------------------------------- 1 | {{ grains['zk_id'] }} 2 | -------------------------------------------------------------------------------- /salt/mesos-slave/templates/slave/containerizers: -------------------------------------------------------------------------------- 1 | docker,mesos 2 | -------------------------------------------------------------------------------- /salt/mesos-slave/templates/slave/hostname: -------------------------------------------------------------------------------- 1 | {{ grains['fqdn'] }} 2 | -------------------------------------------------------------------------------- /salt/mesos-slave/templates/slave/executor_registration_timeout: -------------------------------------------------------------------------------- 1 | 5mins 2 | -------------------------------------------------------------------------------- /salt/mesos-master/templates/mesos-master/hostname: -------------------------------------------------------------------------------- 1 | {{ grains['fqdn'] }} 2 | -------------------------------------------------------------------------------- /architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Marmelatze/saltstack-mesos-test/HEAD/architecture.png -------------------------------------------------------------------------------- /salt/mesos-slave/templates/slave/ip: -------------------------------------------------------------------------------- 1 | {{ grains['ip_interfaces'][pillar['mesos']['interface']][0] }} 2 | -------------------------------------------------------------------------------- /salt/mesos-master/templates/mesos-master/quorum: -------------------------------------------------------------------------------- 1 | {{ (servers.items()|length / 2)|round(0, 'ceil')|int }} 2 | -------------------------------------------------------------------------------- /salt/mesos-master/templates/mesos-master/ip: -------------------------------------------------------------------------------- 1 | {{ grains['ip_interfaces'][pillar['mesos']['interface']][0] }} 2 | -------------------------------------------------------------------------------- /monitoring/proxy_mysql.json: -------------------------------------------------------------------------------- 1 | { 2 | "service": "mysql", 3 | "servicePort": 3306, 4 | "containerPort": 3306 5 | } 6 | -------------------------------------------------------------------------------- /pillar/cadvisor.sls: -------------------------------------------------------------------------------- 1 | # cadvisor config 2 | cadvisor: 3 | # docker-image for cadvisor 4 | image: marmelatze/cadvisor:beta 5 | -------------------------------------------------------------------------------- /salt/cloud/init.sls: -------------------------------------------------------------------------------- 1 | /etc/salt/cloud.providers.d/openstack.conf: 2 | file.managed: 3 | - source: salt://cloud/templates/openstack.conf 4 | -------------------------------------------------------------------------------- /salt/mesos/templates/zk.j2: -------------------------------------------------------------------------------- 1 | zk://{%- for node, zk in masters.items() -%} 2 | {{ zk.ip }}:2181{% if not loop.last %},{% endif -%} 3 | {%- endfor -%}/mesos 4 | -------------------------------------------------------------------------------- /pillar/mine.sls: -------------------------------------------------------------------------------- 1 | # config for salt-mine 2 | mine_functions: 3 | network.ip_addrs: [eth0] 4 | zookeeper: 5 | - mine_function: pillar.get 6 | - zookeeper 7 | -------------------------------------------------------------------------------- /monitoring/proxy_grafana.json: -------------------------------------------------------------------------------- 1 | { 2 | "service": "grafana", 3 | "servicePort": 3000, 4 | "containerPort": 3000, 5 | "timeout": 60, 6 | "external": true 7 | } 8 | -------------------------------------------------------------------------------- /salt/marathon/templates/conf/master: -------------------------------------------------------------------------------- 1 | zk://{%- for server, data in masters.items() -%} 2 | {{ data.ip }}:2181{% if not loop.last %},{% endif -%} 3 | {%- endfor -%}/mesos 4 | -------------------------------------------------------------------------------- /salt/marathon/templates/conf/zk: -------------------------------------------------------------------------------- 1 | zk://{%- for server, data in masters.items() -%} 2 | {{ data.ip }}:2181{% if not loop.last %},{% endif -%} 3 | {%- endfor -%}/marathon 4 | -------------------------------------------------------------------------------- /monitoring/proxy_prometheus.json: -------------------------------------------------------------------------------- 1 | { 2 | "service": "prometheus", 3 | "servicePort": 9090, 4 | "containerPort": 9090, 5 | "timeout": 60, 6 | "external": true 7 | } 8 | -------------------------------------------------------------------------------- /pillar/zookeeper.sls: -------------------------------------------------------------------------------- 1 | # config for zookeeper 2 | zookeeper: 3 | # unique id, should start at 1 4 | id: {{ grains['zk_id'] }} 5 | # ip for running zookeeper on 6 | ip: {{ grains['ip4_interfaces']['eth0'][0] }} 7 | -------------------------------------------------------------------------------- /salt/mesos-slave/templates/slave/attributes: -------------------------------------------------------------------------------- 1 | host:{{ grains['fqdn'] }};{% for attribute, value in salt['pillar.get']('mesos-slave:attributes', {}).items() -%} 2 | {{ attribute }}:{{ value }} 3 | {%- if not loop.last %};{% endif %} 4 | {%- endfor %} 5 | -------------------------------------------------------------------------------- /salt/mine.sls: -------------------------------------------------------------------------------- 1 | /etc/salt/minion.d/mine.conf: 2 | file.managed: 3 | - contents: "mine_interval: 5" 4 | - watch_in: 5 | - service: salt-minion 6 | 7 | salt-minion-service: 8 | service.running: 9 | - name: salt-minion 10 | -------------------------------------------------------------------------------- /salt/mesos/map.jinja: -------------------------------------------------------------------------------- 1 | {% set mesos = salt['grains.filter_by']({ 2 | 'default': { 3 | 'masters': salt['mine.get']('G@roles:master', 'zookeeper', expr_form='compound'), 4 | }, 5 | 'Debian': { 6 | }, 7 | }, 8 | merge=salt['pillar.get']('mesos:lookup'), base='default') %} 9 | -------------------------------------------------------------------------------- /salt/cloud/templates/openstack.conf: -------------------------------------------------------------------------------- 1 | openstack: 2 | # Configure the OpenStack driver 3 | # 4 | identity_url: http://http://192.168.108.207:5000/v2.0/tokens 5 | compute_name: nova 6 | protocol: ipv4 7 | user: admin 8 | password: install 9 | tenant: admin 10 | 11 | provider: admin 12 | -------------------------------------------------------------------------------- /pillar/top.sls: -------------------------------------------------------------------------------- 1 | base: 2 | '*': [] 3 | 4 | 'G@roles:master and G@roles:slave': 5 | - match: compound 6 | - consul 7 | - docker 8 | - weave 9 | - mine 10 | - mesos 11 | - cadvisor 12 | - schub 13 | 14 | 'roles:master': 15 | - match: grain 16 | - zookeeper 17 | -------------------------------------------------------------------------------- /salt/consul/templates/template-upstart: -------------------------------------------------------------------------------- 1 | description "the consul-template service" 2 | start on (filesystem or runlevel [2345] and started consul) 3 | stop on runlevel [!2345] 4 | 5 | #pre-stop exec consul leave 6 | 7 | respawn 8 | 9 | exec /usr/local/bin/consul-template -config=/etc/consul/template.hcl -log-level debug 10 | 11 | -------------------------------------------------------------------------------- /salt/registry/init.sls: -------------------------------------------------------------------------------- 1 | # run a private registry via upstart 2 | /etc/init/registry.conf: 3 | file.managed: 4 | - source: salt://registry/templates/upstart 5 | 6 | 7 | registry: 8 | service.running: 9 | - enable: True 10 | - require: 11 | - file: /etc/init/registry.conf 12 | - watch: 13 | - file: /etc/init/registry.conf 14 | -------------------------------------------------------------------------------- /pillar/schub.sls: -------------------------------------------------------------------------------- 1 | # SCHub specific config 2 | schub: 3 | docker-controller: 4 | # image for the docker-controller 5 | image: marmelatze/docker-controller:1.0-SNAPSHOT 6 | # domain used for the service-gateway 7 | domain: schub-test.local 8 | # ID for the customer read from grains 9 | customer_id: {{ salt['grains.get']('customer_id', 0) }} 10 | -------------------------------------------------------------------------------- /pillar/mesos.sls: -------------------------------------------------------------------------------- 1 | # mesos config 2 | mesos: 3 | # interface for running mesos on 4 | interface: eth0 5 | 6 | # mesos-slave config 7 | mesos-slave: 8 | # attributes which are saved to /etc/mesos-slave/attributes and can be quried by marathon 9 | attributes: 10 | rack: a 11 | foo: bar 12 | customer: customer-{{ salt['grains.get']('customer_id', 0) }} 13 | -------------------------------------------------------------------------------- /salt/consul/templates/dnsmasq: -------------------------------------------------------------------------------- 1 | server=/consul/127.0.0.1#8600 2 | address=/gateway/{{ pillar['docker']['gw'] }} 3 | address=/.{{ pillar['schub']['domain'] }}/{{ pillar['docker']['gw'] }} 4 | 5 | {% for server, addrs in salt['mine.get']('*', 'network.ip_addrs').items() %} 6 | address=/{{ server }}/{{ addrs[0] }} 7 | 8 | {% endfor %} 9 | local=/localnet/ 10 | local=/localdomain/ 11 | -------------------------------------------------------------------------------- /salt/consul/templates/server/docker.json: -------------------------------------------------------------------------------- 1 | { 2 | "service": { 3 | "name": "docker", 4 | "port": 2375, 5 | "checks": [ 6 | { 7 | "http": "http://localhost:2375/info", 8 | "interval": "30s" 9 | } 10 | ], 11 | "tags": ["customer-{{ pillar['schub']['customer_id'] }}"] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /salt/cadvisor/init.sls: -------------------------------------------------------------------------------- 1 | # create upstart file for cadvisor 2 | 3 | /etc/init/cadvisor.conf: 4 | file.managed: 5 | - source: salt://cadvisor/templates/upstart 6 | - template: jinja 7 | 8 | cadvisor: 9 | service: 10 | - running 11 | - enable: True 12 | - require: 13 | - file: /etc/init/cadvisor.conf 14 | - watch: 15 | - file: /etc/init/cadvisor.conf 16 | -------------------------------------------------------------------------------- /salt/schub/init.sls: -------------------------------------------------------------------------------- 1 | # create and enable docker-controller service 2 | /etc/init/docker-controller.conf: 3 | file.managed: 4 | - source: salt://schub/templates/docker-controller.conf 5 | - template: jinja 6 | 7 | docker-controller: 8 | service.running: 9 | - require: 10 | - file: /etc/init/docker-controller.conf 11 | - watch: 12 | - file: /etc/init/docker-controller.conf 13 | -------------------------------------------------------------------------------- /salt/marathon/templates/upstart: -------------------------------------------------------------------------------- 1 | description "Marathon scheduler for Mesos" 2 | 3 | start on runlevel [2345] 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | respawn limit 10 5 8 | 9 | script 10 | 11 | export LIBPROCESS_IP={{ grains['ip_interfaces'][pillar['mesos']['interface']][0] }} 12 | /usr/bin/marathon --ha --hostname {{ grains['fqdn'] }} --assets_path /usr/share/marathon-ui 13 | 14 | end script 15 | -------------------------------------------------------------------------------- /salt/registrator/init.sls: -------------------------------------------------------------------------------- 1 | # create and enable registrator service 2 | /etc/init/registrator.conf: 3 | file.managed: 4 | - source: salt://registrator/templates/upstart 5 | - template: jinja 6 | 7 | registrator: 8 | service: 9 | - running 10 | - require: 11 | - file: /etc/init/registrator.conf 12 | - service: consul 13 | - watch: 14 | - file: /etc/init/registrator.conf 15 | -------------------------------------------------------------------------------- /salt/top.sls: -------------------------------------------------------------------------------- 1 | base: 2 | '*': [] 3 | 4 | 5 | 'bastion': 6 | - cloud 7 | 8 | 'G@roles:master and G@roles:slave': 9 | - match: compound 10 | - mesos 11 | - consul 12 | 13 | 14 | 'roles:master': 15 | - match: grain 16 | - mesos-master 17 | - marathon 18 | 19 | 'roles:slave': 20 | - match: grain 21 | - mesos-slave 22 | - docker 23 | - weave 24 | - nginx 25 | - cadvisor 26 | - registrator 27 | - schub 28 | 29 | 'G@host_id == 0': 30 | - match: compound 31 | - registry 32 | -------------------------------------------------------------------------------- /salt/consul/templates/upstart: -------------------------------------------------------------------------------- 1 | description "the consul service" 2 | start on (filesystem or runlevel [2345] and started docker) 3 | stop on runlevel [!2345] 4 | 5 | #pre-stop exec consul maint -enable 6 | 7 | respawn 8 | 9 | setuid consul 10 | setgid consul 11 | 12 | script 13 | 14 | export GOMAXPROCS=`nproc` 15 | 16 | 17 | exec consul agent \ 18 | "--config-dir=/etc/consul/server" \ 19 | "--data-dir=/data/consul" \ 20 | "--ui-dir=/usr/share/consul/ui/dist" \ 21 | ${CONSUL_FLAGS} 22 | end script 23 | 24 | #post-start exec consul maint -disable 25 | -------------------------------------------------------------------------------- /pillar/weave.sls: -------------------------------------------------------------------------------- 1 | # weave overlay network config 2 | weave: 3 | version: 0.11.0 4 | # hash of the downloaded binary 5 | hash: md5=4cb8e60c53ef148b8b1ef5315f071af1 6 | host_id: {{ grains['host_id'] }} 7 | bridge_cidr: 10.{{ salt['grains.get']('customer_id', 0) }}.0.{{ grains['host_id'] }}/8 8 | network_cidr: 10.{{ salt['grains.get']('customer_id', 0) }}.{{ grains['host_id'] }}.0/24 9 | # password for authenticating other peers 10 | password: install 11 | # config for weave scope 12 | scope: 13 | version: 0.2.0 14 | hash: md5=720d489fce75031261fe251af1ee589b 15 | -------------------------------------------------------------------------------- /scaling/marathon-scaler.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "/marathon-scaler", 3 | "cpus": 0.1, 4 | "mem": 256 , 5 | "instances": 1, 6 | "env": { 7 | "SERVICE_NAME": "marathon-scaler", 8 | "SERVICE_CHECK_TTL": "30s" 9 | }, 10 | "constraints": [["customer", "CLUSTER", "customer-0"]], 11 | "container": { 12 | "type": "DOCKER", 13 | "docker": { 14 | "image": "marmelatze/marathon-scaler:1.0-SNAPSHOT", 15 | "network": "BRIDGE" 16 | } 17 | }, 18 | "args": ["-monitoring=http://gateway:9090", "-marathonURI=http://gateway:8080", "-consul=http://gateway:8500"] 19 | } 20 | -------------------------------------------------------------------------------- /pillar/consul.sls: -------------------------------------------------------------------------------- 1 | # consul key value store config 2 | consul: 3 | version: 0.5.2 4 | hash: 5 | # hash for the consul-ui archive 6 | ui: "md5=eb98ba602bc7e177333eb2e520881f4f" 7 | # encryption key for serf traffic 8 | encrypt: "isRJBhPfMKC3DeS3ZNbDXw==" 9 | # consul-template config 10 | template: 11 | # version to be installed 12 | version: 0.10.0 13 | # hash for the consul-template archive 14 | hash: "md5=c09d9e77ff079e17b7097af882eab5d6" 15 | # server or client mode 16 | mode: {% if 'master' in salt['grains.get']('roles', []) %}server{% else %}client{% endif %} 17 | -------------------------------------------------------------------------------- /salt/mesos/templates/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | {% if 'master' in salt['grains.get']('roles', []) %} 5 | service marathon stop 6 | service mesos-master stop 7 | service zookeeper stop 8 | {% endif %} 9 | {% if 'slave' in salt['grains.get']('roles', []) %} 10 | service mesos-slave stop 11 | {% endif %} 12 | sleep 1 13 | 14 | rm -R /var/lib/mesos 15 | rm -R /var/lib/zookeeper/version-2 16 | rm -R /tmp/mesos 17 | 18 | {% if 'master' in salt['grains.get']('roles', []) %} 19 | service zookeeper start 20 | sleep 30 21 | service mesos-master start 22 | sleep 10 23 | {% endif %} 24 | 25 | {% if 'slave' in salt['grains.get']('roles', []) %} 26 | service mesos-slave start 27 | {% endif %} 28 | -------------------------------------------------------------------------------- /salt/registrator/templates/upstart: -------------------------------------------------------------------------------- 1 | description "Registrator" 2 | 3 | start on filesystem and started docker and started consul 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | respawn limit 10 10 8 | 9 | start on started docker 10 | stop on stopped docker 11 | 12 | pre-start script 13 | /usr/bin/docker rm -f registrator ||true 14 | end script 15 | 16 | script 17 | /usr/bin/docker run \ 18 | --restart=always \ 19 | --name=registrator \ 20 | -v /var/run/docker.sock:/tmp/docker.sock \ 21 | -h {{ grains['fqdn'] }} \ 22 | marmelatze/registrator:latest -ttl 30 -ttl-refresh 20 -tags customer-{{ pillar['schub']['customer_id'] }} -internal consul://{{ pillar['docker']['gw'] }}:8500 23 | end script 24 | -------------------------------------------------------------------------------- /salt/cadvisor/templates/upstart: -------------------------------------------------------------------------------- 1 | description "cadvsior" 2 | 3 | start on filesystem and started docker 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | respawn limit 10 10 8 | 9 | start on started docker 10 | stop on stopped docker 11 | 12 | pre-start script 13 | /usr/bin/docker rm -f cadvisor ||true 14 | /usr/bin/docker pull {{ pillar['cadvisor']['image'] }} 15 | end script 16 | 17 | script 18 | docker run \ 19 | --volume=/:/rootfs:ro \ 20 | --volume=/var/run:/var/run:rw \ 21 | --volume=/sys:/sys:ro \ 22 | --volume=/var/lib/docker/:/var/lib/docker:ro \ 23 | --name=cadvisor \ 24 | -p 4040:8080 \ 25 | {{ pillar['cadvisor']['image'] }} --docker_metadata_env="MARATHON_APP_ID,MARATHON_APP_VERSION,MESOS_TASK_ID" 26 | end script 27 | -------------------------------------------------------------------------------- /salt/weave/templates/upstart: -------------------------------------------------------------------------------- 1 | description "weave router" 2 | 3 | start on filesystem and started docker 4 | stop on runlevel [!2345] 5 | 6 | 7 | env PEERS=" 8 | {%- for server, addrs in salt['mine.get']('* and not ' ~ grains['fqdn'], 'network.ip_addrs', expr_form='compound').items() -%} 9 | {{ addrs[0] }}{% if not loop.last %} {% endif %} 10 | {%- endfor %}" 11 | 12 | env WEAVE_PASSWORD="{{ pillar['weave']['password'] }}" 13 | 14 | respawn 15 | respawn limit 10 10 16 | 17 | script 18 | [ -e /etc/default/weave ] && . /etc/default/weave 19 | /usr/bin/docker rm -f weave ||true 20 | /usr/local/bin/weave launch -password $WEAVE_PASSWORD $PEERS 21 | /usr/bin/docker logs -f weave 22 | end script 23 | 24 | pre-stop exec /usr/local/bin/weave stop 25 | -------------------------------------------------------------------------------- /salt/weave/weave-scope.sls: -------------------------------------------------------------------------------- 1 | weave-scope: 2 | file.managed: 3 | - name: /usr/local/bin/weave-scope 4 | - source: https://github.com/weaveworks/scope/releases/download/v{{ pillar['weave']['scope']['version'] }}/scope 5 | - source_hash: {{ pillar['weave']['scope']['hash'] }} 6 | - mode: 0755 7 | service: 8 | - running 9 | - enable: True 10 | - require: 11 | - file: weave-scope 12 | - file: /etc/init/weave-scope.conf 13 | - watch: 14 | - file: /usr/local/bin/weave-scope 15 | 16 | /etc/init/weave-scope.conf: 17 | file.managed: 18 | - source: salt://weave/templates/scope-upstart 19 | - template: jinja 20 | - require: 21 | - file: weave-scope 22 | - watch_in: 23 | - service: weave-scope 24 | -------------------------------------------------------------------------------- /salt/nginx/init.sls: -------------------------------------------------------------------------------- 1 | # install and configure nginx as service gateway 2 | nginx: 3 | pkgrepo.managed: 4 | - humannane: Nginx Devel 5 | - ppa: chris-lea/nginx-devel 6 | - keyserver: hkp://keyserver.ubuntu.com:80 7 | - require_in: 8 | - pkg: nginx 9 | pkg.latest: 10 | - refresh: True 11 | service: 12 | - running 13 | - enable: True 14 | - require: 15 | - pkg: nginx 16 | 17 | /etc/nginx/nginx.conf: 18 | file.managed: 19 | - source: salt://nginx/templates/nginx.conf 20 | - watch_in: 21 | - service: nginx 22 | - require: 23 | - pkg: nginx 24 | 25 | /etc/nginx/streams-enabled: 26 | file.directory: 27 | - require: 28 | - pkg: nginx 29 | 30 | /etc/nginx/sited-enabled/default: 31 | file.absent: [] 32 | -------------------------------------------------------------------------------- /salt/schub/templates/docker-controller.conf: -------------------------------------------------------------------------------- 1 | description "Docker Controller" 2 | 3 | start on filesystem and started docker and started consul 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | respawn limit 10 10 8 | 9 | pre-start script 10 | /usr/bin/docker rm -f docker-controller ||true 11 | /usr/bin/docker pull {{ pillar['schub']['docker-controller']['image'] }} 12 | end script 13 | 14 | script 15 | /usr/bin/docker run \ 16 | --restart=always \ 17 | --name=docker-controller \ 18 | -v /var/run/docker.sock:/var/run/docker.sock \ 19 | {{ pillar['schub']['docker-controller']['image'] }} -collector=docker:///var/run/docker.sock -storage=consul://{{ pillar['docker']['gw'] }}:8500 -cadvisor=http://{{ pillar['docker']['gw'] }}:4040 -interval=2 20 | end script 21 | -------------------------------------------------------------------------------- /salt/registry/templates/upstart: -------------------------------------------------------------------------------- 1 | description "Docker Registry" 2 | 3 | # Start just after the System-V jobs (rc) to ensure networking and zookeeper 4 | # are started. This is as simple as possible to ensure compatibility with 5 | # Ubuntu, Debian, CentOS, and RHEL distros. See: 6 | # http://upstart.ubuntu.com/cookbook/#standard-idioms 7 | 8 | start on started docker 9 | stop on stopped docker 10 | 11 | respawn 12 | respawn limit 10 10 13 | 14 | pre-start script 15 | /usr/bin/docker rm -f registry ||true 16 | end script 17 | 18 | script 19 | docker run \ 20 | --restart=always \ 21 | --name=registry \ 22 | -e STORAGE_PATH=/registry \ 23 | -e SEARCH_BACKEND=sqlalchemy \ 24 | -v /data/registry:/registry \ 25 | -p 5000:5000 \ 26 | registry:0.9.1 27 | end script 28 | -------------------------------------------------------------------------------- /salt/docker/templates/docker_proxy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | export DOCKER_HOST="localhost:2375" 6 | echo "$@" >> /var/log/mesos/docker 7 | 8 | #socketplane_commands=("run" "start" "stop" "rm") 9 | #for cmd in run start stop rm 10 | #do 11 | # if [ "$cmd" == "$1" ]; then 12 | # socketplane "$@" 13 | # exit $? 14 | # fi 15 | #done; 16 | 17 | # ensure deleting container via socketplane 18 | if [ "rm" == "$1" ]; then 19 | for arg in "${@:2}" 20 | do 21 | if ! [[ $arg =~ ^- ]]; then 22 | cid=$(docker ps -a --no-trunc=true | grep $arg | awk {' print $1'}) 23 | docker "$@" 24 | result=$? 25 | if [[ $result == 0 ]]; then 26 | curl -s -X DELETE http://localhost:6675/v0.1/connections/$cid 27 | fi; 28 | break; 29 | fi; 30 | done; 31 | else 32 | docker "$@" 33 | result=$? 34 | fi; 35 | 36 | 37 | #docker "$@" 38 | exit $result 39 | -------------------------------------------------------------------------------- /salt/marathon/init.sls: -------------------------------------------------------------------------------- 1 | # install and configure marathon 2 | {% from "mesos/map.jinja" import mesos with context %} 3 | 4 | marathon: 5 | service.running: 6 | - require: 7 | - pkgrepo: mesos-repo 8 | 9 | /etc/init/marathon.conf: 10 | file.managed: 11 | - source: salt://marathon/templates/upstart 12 | - template: jinja 13 | - watch_in: 14 | - service: marathon 15 | 16 | /etc/marathon/conf: 17 | file.recurse: 18 | - source: salt://marathon/templates/conf 19 | - makedirs: True 20 | - template: jinja 21 | - watch_in: 22 | - service: marathon 23 | - context: 24 | masters: {{ mesos.masters }} 25 | 26 | # use customer marathon-ui 27 | /usr/share/marathon-ui: 28 | git.latest: 29 | - name: https://github.com/Marmelatze/marathon-ui.git 30 | - rev: stats 31 | - target: /usr/share/marathon-ui 32 | -------------------------------------------------------------------------------- /salt/consul/templates/server/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bootstrap": false, 3 | {% if pillar['consul']['mode'] == "server" %}"bootstrap_expect": {{ expect }},{% endif %} 4 | "server": {% if pillar['consul']['mode'] == "server" %}true{% else %}false{% endif%}, 5 | "datacenter": "test", 6 | "data_dir": "/data/consul", 7 | "encrypt": "{{ pillar['consul']['encrypt'] }}", 8 | "log_level": "INFO", 9 | "enable_syslog": true, 10 | "advertise_addr": "{{ grains['ip_interfaces'][pillar['mesos']['interface']][0] }}", 11 | "addresses": { 12 | "http": "0.0.0.0" 13 | }, 14 | "retry_max": 0, 15 | "retry_interval": "10s", 16 | "retry_join": [ 17 | {%- for server, addrs in salt['mine.get']('G@roles:master and not ' ~ grains['fqdn'], 'network.ip_addrs', expr_form='compound').items() %} 18 | "{{ addrs[0] }}"{% if not loop.last %},{% endif %} 19 | {%- endfor %} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /salt/consul/templates/templates/nginx-stream.ctmpl: -------------------------------------------------------------------------------- 1 | {{ "{{$customer := \"customer-" ~ customer_id ~ "\" }}" }} 2 | {{ "{{$gateway := \"" ~ pillar['docker']['gw'] ~ "\"}}" }} 3 | 4 | {% raw %} 5 | {{range ls "proxy/tcp"}} 6 | {{with $d := .Value | parseJSON}} 7 | {{if $d}} 8 | {{if service (print $customer "." $d.service ":" $d.containerPort)}} 9 | upstream backend-{{$customer}}-{{$d.service}}-{{$d.servicePort}} { 10 | {{range service (print $customer "." $d.service ":" $d.containerPort)}} 11 | server {{.Address}}:{{.Port}}; 12 | {{end}} 13 | } 14 | server { 15 | listen {{if not $d.external}}{{$gateway}}:{{end}}{{$d.servicePort}}; 16 | proxy_pass backend-{{$customer}}-{{$d.service}}-{{$d.servicePort}}; 17 | } 18 | {{end}} 19 | {{end}} 20 | {{end}} 21 | {{end}} 22 | {% endraw %} 23 | -------------------------------------------------------------------------------- /salt/mesos-slave/init.sls: -------------------------------------------------------------------------------- 1 | {% from "mesos/map.jinja" import mesos with context %} 2 | 3 | mesos: 4 | pkg.installed: 5 | - require: 6 | - pkgrepo: mesos-repo 7 | 8 | # disable zookeeper and mesos-master on slaves 9 | {% if 'master' not in salt['grains.get']('roles', []) %} 10 | zookeeper: 11 | service.dead: 12 | - enable: False 13 | 14 | mesos-master: 15 | service.dead: 16 | - enable: False 17 | 18 | {% endif %} 19 | 20 | mesos-slave: 21 | service.running: 22 | - require: 23 | - pkg: docker 24 | 25 | /etc/mesos-slave: 26 | file.recurse: 27 | - source: salt://mesos-slave/templates/slave 28 | - template: jinja 29 | - watch_in: 30 | - service: mesos-slave 31 | - cmd: mesos-slave-unlink 32 | - context: 33 | masters: {{ mesos.masters }} 34 | 35 | mesos-slave-unlink: 36 | cmd.wait: 37 | - name: rm -f /tmp/mesos/meta/slaves/latest 38 | -------------------------------------------------------------------------------- /pillar/docker.sls: -------------------------------------------------------------------------------- 1 | # docker config 2 | docker: 3 | host_id: {{ grains['host_id'] }} 4 | # gateway for all containers (weave interface) 5 | gw: 10.{{ salt['grains.get']('customer_id', 0) }}.0.{{ grains['host_id'] }} 6 | # private registry 7 | registry: 10.0.0.1:5000 8 | # options passed to docker-daemon 9 | options: 10 | # mtu 1480 for weave overhead 11 | mtu: 1480 12 | bridge: weave 13 | # weave ip range 14 | fixed-cidr: 10.{{ salt['grains.get']('customer_id', 0) }}.{{ grains['host_id'] }}.0/24 15 | bip: ~ 16 | # same as gateway, where dnsmasq runs 17 | dns: 10.{{ salt['grains.get']('customer_id', 0) }}.0.{{ grains['host_id'] }} 18 | 19 | # set storage to overlay or aufs depending on kernel version 20 | {% set kernel = grains['kernelrelease'].split(".") %} 21 | {% if kernel[0]|int >= 3 and kernel[1]|int >= 18 %} 22 | storage: overlay 23 | {% else %} 24 | storage: aufs 25 | {% endif %} 26 | -------------------------------------------------------------------------------- /salt/docker/templates/default: -------------------------------------------------------------------------------- 1 | # Docker Upstart and SysVinit configuration file 2 | 3 | # Customize location of Docker binary (especially for development testing). 4 | #DOCKER="/usr/local/bin/docker" 5 | 6 | #. /run/flannel/subnet.env 7 | # Use DOCKER_OPTS to modify the daemon startup options. 8 | #DOCKER_OPTS="--ip-masq=false --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}" 9 | DOCKER_OPTS="{% for opt, value in pillar['docker']['options'].items() if value is not none -%} 10 | --{{ opt }}={{ value }} \ 11 | {% endfor -%} 12 | -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375 \ 13 | -s {{ pillar['docker']['storage'] }} \ 14 | --insecure-registry {{ pillar['docker']['registry'] }}" 15 | 16 | # If you need Docker to use an HTTP proxy, it can also be specified here. 17 | #export http_proxy="http://127.0.0.1:3128/" 18 | 19 | # This is also a handy place to tweak where Docker's temporary files go. 20 | #export TMPDIR="/mnt/bigdrive/docker-tmp" 21 | -------------------------------------------------------------------------------- /monitoring/config_prometheus.yml: -------------------------------------------------------------------------------- 1 | # my global config 2 | global: 3 | scrape_interval: 15s 4 | evaluation_interval: 30s 5 | # scrape_timeout is set to the global default (10s). 6 | 7 | scrape_configs: 8 | - job_name: prometheus 9 | target_groups: 10 | - targets: ['localhost:9090'] 11 | 12 | - job_name: cadvisor 13 | consul_sd_configs: 14 | - server: 'gateway:8500' 15 | services: ['cadvisor'] 16 | 17 | # relabel address to use ServiceAddress instead of Address 18 | relabel_configs: 19 | - target_label: __address__ 20 | source_labels: ['__meta_consul_service_address', '__meta_consul_service_port'] 21 | separator: ':' 22 | regex: '.*' 23 | replacement: '${0}' 24 | 25 | - target_label: node 26 | source_labels: ['__meta_consul_node'] 27 | regex: '.*' 28 | replacement: '${0}' 29 | 30 | - target_label: customer 31 | source_labels: ['__meta_consul_tags'] 32 | regex: ',customer-([0-9]+),' 33 | replacement: '${1}' 34 | -------------------------------------------------------------------------------- /salt/weave/templates/scope-upstart: -------------------------------------------------------------------------------- 1 | description "weave scoper" 2 | 3 | start on filesystem and started docker 4 | stop on runlevel [!2345] 5 | 6 | 7 | # Start just after the System-V jobs (rc) to ensure networking and zookeeper 8 | # are started. This is as simple as possible to ensure compatibility with 9 | # Ubuntu, Debian, CentOS, and RHEL distros. See: 10 | # http://upstart.ubuntu.com/cookbook/#standard-idioms 11 | env PEERS=" 12 | {%- for server, addrs in salt['mine.get']('* and not ' ~ grains['fqdn'], 'network.ip_addrs', expr_form='compound').items() -%} 13 | {{ addrs[0] }}{% if not loop.last %} {% endif %} 14 | {%- endfor %}" 15 | 16 | 17 | respawn 18 | respawn limit 10 10 19 | 20 | script 21 | [ -e /etc/default/weavescope ] && . /etc/default/weavescope 22 | /usr/bin/docker rm -f weavescope ||true 23 | /usr/local/bin/weave-scope launch -http.address=":4043" $PEERS 24 | /usr/bin/docker logs -f weavescope 25 | end script 26 | 27 | pre-stop exec /usr/local/bin/weave-scope stop 28 | -------------------------------------------------------------------------------- /salt/consul/templates/templates/nginx.ctmpl: -------------------------------------------------------------------------------- 1 | {{ "{{$customer := \"customer-" ~ customer_id ~ "\" }}" }} 2 | {{ "{{$gateway := \"" ~ pillar['docker']['gw'] ~ "\" }}" }} 3 | {% raw %} 4 | {{range ls "proxy/http"}} 5 | {{with $d := .Value | parseJSON}} 6 | {{if $d}} 7 | {{if service (print $customer "." $d.service ":" $d.containerPort)}} 8 | upstream backend-{{$customer}}-{{$d.service}}-{{$d.servicePort}} { 9 | {{range service (print $customer "." $d.service ":" $d.containerPort)}} 10 | server {{.Address}}:{{.Port}}; 11 | {{end}} 12 | } 13 | server { 14 | listen {{if not $d.external}}{{$gateway}}:{{end}}{{$d.servicePort}}; 15 | location / { 16 | proxy_read_timeout {{if $d.timeout }}{{$d.timeout}}{{ else }}10{{end}}; 17 | proxy_pass http://backend-{{$customer}}-{{$d.service}}-{{$d.servicePort}}; 18 | } 19 | } 20 | {{end}} 21 | {{end}} 22 | {{end}} 23 | {{end}} 24 | {% endraw %} 25 | -------------------------------------------------------------------------------- /salt/mesos-master/init.sls: -------------------------------------------------------------------------------- 1 | {% from "mesos/map.jinja" import mesos with context %} 2 | 3 | mesosphere: 4 | pkg.installed: 5 | - require: 6 | - pkgrepo: mesos-repo 7 | 8 | /etc/zookeeper/conf/myid: 9 | file.managed: 10 | - source: salt://mesos-master/templates/zookeeper/myid 11 | - template: jinja 12 | - makedirs: True 13 | - watch_in: 14 | - service: zookeeper 15 | 16 | /etc/zookeeper/conf/zoo.cfg: 17 | file.managed: 18 | - source: salt://mesos-master/templates/zookeeper/zoo.cfg 19 | - template: jinja 20 | - makedirs: True 21 | - watch_in: 22 | - service: zookeeper 23 | - context: 24 | servers: {{ mesos.masters }} 25 | 26 | /etc/mesos-master: 27 | file.recurse: 28 | - source: salt://mesos-master/templates/mesos-master 29 | - template: jinja 30 | - watch_in: 31 | - service: mesos-master 32 | - context: 33 | servers: {{ mesos.masters }} 34 | 35 | zookeeper: 36 | service.running: [] 37 | 38 | mesos-master: 39 | service.running: [] 40 | -------------------------------------------------------------------------------- /salt/mesos/init.sls: -------------------------------------------------------------------------------- 1 | {% from "mesos/map.jinja" import mesos with context %} 2 | 3 | mesos-repo: 4 | pkgrepo.managed: 5 | - humanname: Mesos Repo 6 | - name: deb http://repos.mesosphere.io/{{ grains['os']|lower }} {{ grains['oscodename'] }} main 7 | - file: /etc/apt/sources.list.d/mesosphere.list 8 | - keyid: E56151BF 9 | - keyserver: hkp://keyserver.ubuntu.com:80 10 | 11 | /etc/mesos/zk: 12 | file.managed: 13 | - source: salt://mesos/templates/zk.j2 14 | - template: jinja 15 | - makedirs: True 16 | - context: 17 | masters: {{ mesos.masters }} 18 | 19 | # cleaup-script for resetting the cluster 20 | /root/cleanup.sh: 21 | file.managed: 22 | - source: salt://mesos/templates/cleanup.sh 23 | - template: jinja 24 | - mode: 0755 25 | 26 | 27 | # make entrys for every node in the cluster for named based address resolving 28 | {% for server, addrs in salt['mine.get']('*', 'network.ip_addrs').items() %} 29 | 30 | host-{{ server}}: 31 | host.present: 32 | - name: {{ server }} 33 | - ip: {{ addrs[0] }} 34 | 35 | {% endfor %} 36 | -------------------------------------------------------------------------------- /salt/docker/init.sls: -------------------------------------------------------------------------------- 1 | # install and configure docker 2 | docker-repo: 3 | pkgrepo.managed: 4 | - humanname: Docker 5 | - name: deb https://get.docker.com/{{ grains['os']|lower }} docker main 6 | - file: /etc/apt/sources.list.d/docker.list 7 | - keyid: 36A1D7869245C8950F966E92D8576A8BA88D21E9 8 | - keyserver: hkp://p80.pool.sks-keyservers.net:80 9 | - require_in: 10 | - pkg: lxc-docker 11 | 12 | /etc/default/docker: 13 | file.managed: 14 | - source: salt://docker/templates/default 15 | - template: jinja 16 | - require: 17 | - pkg: docker 18 | 19 | /etc/init/docker.conf: 20 | file.managed: 21 | - source: salt://docker/templates/upstart 22 | - template: jinja 23 | - require: 24 | - pkg: docker 25 | 26 | docker: 27 | pkg.installed: 28 | - name: lxc-docker 29 | service.running: 30 | - watch: 31 | - file: /etc/default/docker 32 | - require: 33 | - pkg: docker 34 | 35 | # install required packages when using aufs 36 | {% if pillar['docker']['storage'] == "aufs" %} 37 | linux-image-extra-{{ grains['kernelrelease'] }}: 38 | pkg.installed: 39 | - require_in: docker 40 | 41 | linux-image-extra-virtual: 42 | pkg.installed: 43 | - require_in: docker 44 | 45 | {% endif %} 46 | -------------------------------------------------------------------------------- /salt/consul/templates/template.hcl: -------------------------------------------------------------------------------- 1 | consul = "{{ pillar['docker']['gw'] }}:8500" 2 | 3 | template { 4 | source = "/etc/consul/templates/global/nginx.ctmpl" 5 | destination = "/etc/nginx/sites-enabled/global-service-proxy" 6 | command = "service nginx restart || true" 7 | } 8 | 9 | template { 10 | source = "/etc/consul/templates/global/nginx-stream.ctmpl" 11 | destination = "/etc/nginx/streams-enabled/global-service-proxy" 12 | command = "service nginx restart || true" 13 | } 14 | 15 | template { 16 | source = "/etc/consul/templates/global/nginx-web.ctmpl" 17 | destination = "/etc/nginx/sites-enabled/global-web-proxy" 18 | command = "service nginx restart || true" 19 | } 20 | 21 | {% if pillar['schub']['customer_id'] != 0 %} 22 | template { 23 | source = "/etc/consul/templates/customer/nginx.ctmpl" 24 | destination = "/etc/nginx/sites-enabled/service-proxy" 25 | command = "service nginx restart || true" 26 | } 27 | 28 | template { 29 | source = "/etc/consul/templates/customer/nginx-stream.ctmpl" 30 | destination = "/etc/nginx/streams-enabled/service-proxy" 31 | command = "service nginx restart || true" 32 | } 33 | 34 | template { 35 | source = "/etc/consul/templates/customer/nginx-web.ctmpl" 36 | destination = "/etc/nginx/sites-enabled/web-proxy" 37 | command = "service nginx restart || true" 38 | } 39 | {% endif %} 40 | -------------------------------------------------------------------------------- /salt/consul/templates/templates/nginx-web.ctmpl: -------------------------------------------------------------------------------- 1 | {{ "{{$customer_id := \"" ~ customer_id ~ "\" }}" }} 2 | {{ "{{$customer := \"customer-" ~ customer_id ~ "\"}}" }} 3 | {{ "{{$gateway := \"" ~ pillar['docker']['gw'] ~ "\"}}" }} 4 | {{ "{{$domain := \"" ~ pillar['schub']['domain'] ~ "\"}}" }} 5 | {% raw %} 6 | 7 | {{with $customer_data := key (print "customer/" $customer_id) | parseJSON }} 8 | {{if $customer}} 9 | {{range ls "proxy/web"}} 10 | {{with $d := .Value | parseJSON}} 11 | {{if $d}} 12 | {{if service (print $customer "." $d.service ":" $d.containerPort)}} 13 | upstream web-backend-{{$customer}}-{{$d.service}}-{{$d.containerPort}} { 14 | {{range service (print $customer "." $d.service ":" $d.containerPort)}} 15 | server {{.Address}}:{{.Port}}; 16 | {{end}} 17 | } 18 | server { 19 | listen {{if not $d.external}}{{$gateway}}:{{end}}{{$d.servicePort}}; 20 | server_name {{$d.service}}.{{$customer_data.name}}.{{$domain}}; 21 | 22 | location / { 23 | proxy_read_timeout {{if $d.timeout }}{{$d.timeout}}{{ else }}10{{end}}; 24 | proxy_pass http://web-backend-{{$customer}}-{{$d.service}}-{{$d.containerPort}}; 25 | proxy_set_header X-Real-IP $remote_addr; 26 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 27 | proxy_set_header Host $http_host; 28 | } 29 | } 30 | {{end}} 31 | {{end}} 32 | {{end}} 33 | {{end}} 34 | {{end}} 35 | {{end}} 36 | 37 | {% endraw %} 38 | -------------------------------------------------------------------------------- /monitoring/marathon.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "/monitoring", 3 | "apps": [ 4 | { 5 | "id": "/monitoring/grafana", 6 | "dependencies": ["/monitoring/prometheus"], 7 | 8 | "cpus": 0.1, 9 | "mem": 256 , 10 | "instances": 1, 11 | "env": { 12 | "SERVICE_NAME": "grafana", 13 | "SERVICE_CHECK_TTL": "30s", 14 | "GF_SECURITY_ADMIN_PASSWORD": "install", 15 | "GF_DATABASE_TYPE": "mysql", 16 | "GF_DATABASE_HOST": "gateway:3306", 17 | "GF_DATABASE_NAME": "grafana", 18 | "GF_DATABASE_USER": "grafana", 19 | "GF_DATABASE_PASSWORD": "grafana", 20 | "GF_AUTH_ANONYMOUS_ENABLED": "true" 21 | }, 22 | "constraints": [["customer", "CLUSTER", "customer-0"]], 23 | "container": { 24 | "type": "DOCKER", 25 | "docker": { 26 | "image": "marmelatze/grafana-prometheus:latest", 27 | "network": "BRIDGE" 28 | } 29 | } 30 | }, 31 | { 32 | "id": "/monitoring/prometheus", 33 | "cpus": 0.2, 34 | "mem": 512, 35 | "instances": 1, 36 | "env": { 37 | "SERVICE_NAME": "prometheus", 38 | "SERVICE_CHECK_TTL": "30s" 39 | }, 40 | "args": [ 41 | "-config.file=/mnt/mesos/sandbox/prometheus?raw" 42 | ], 43 | "uris": [ 44 | "http://gateway:8500/v1/kv/config/prometheus?raw" 45 | ], 46 | "constraints": [["customer", "CLUSTER", "customer-0"]], 47 | "container": { 48 | "type": "DOCKER", 49 | "docker": { 50 | "image": "prom/prometheus:master", 51 | "network": "BRIDGE", 52 | "forcePullImage": true 53 | } 54 | } 55 | } 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /salt/docker/templates/upstart: -------------------------------------------------------------------------------- 1 | description "Docker daemon" 2 | 3 | start on (local-filesystems and net-device-up IFACE!=lo) 4 | stop on runlevel [!2345] 5 | limit nofile 524288 1048576 6 | limit nproc 524288 1048576 7 | 8 | respawn 9 | 10 | pre-start script 11 | # see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount 12 | if grep -v '^#' /etc/fstab | grep -q cgroup \ 13 | || [ ! -e /proc/cgroups ] \ 14 | || [ ! -d /sys/fs/cgroup ]; then 15 | exit 0 16 | fi 17 | if ! mountpoint -q /sys/fs/cgroup; then 18 | mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup 19 | fi 20 | ( 21 | cd /sys/fs/cgroup 22 | for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do 23 | mkdir -p $sys 24 | if ! mountpoint -q $sys; then 25 | if ! mount -n -t cgroup -o $sys cgroup $sys; then 26 | rmdir $sys || true 27 | fi 28 | fi 29 | done 30 | ) 31 | end script 32 | 33 | script 34 | # modify these in /etc/default/$UPSTART_JOB (/etc/default/docker) 35 | DOCKER=/usr/bin/$UPSTART_JOB 36 | DOCKER_OPTS= 37 | if [ -f /etc/default/$UPSTART_JOB ]; then 38 | . /etc/default/$UPSTART_JOB 39 | fi 40 | exec "$DOCKER" -d $DOCKER_OPTS 41 | end script 42 | 43 | # Don't emit "started" event until docker.sock is ready. 44 | # See https://github.com/docker/docker/issues/6647 45 | post-start script 46 | DOCKER_OPTS= 47 | if [ -f /etc/default/$UPSTART_JOB ]; then 48 | . /etc/default/$UPSTART_JOB 49 | fi 50 | if ! printf "%s" "$DOCKER_OPTS" | grep -qE -e '-H|--host'; then 51 | while ! [ -e /var/run/docker.sock ]; do 52 | initctl status $UPSTART_JOB | grep -qE "(stop|respawn)/" && exit 1 53 | echo "Waiting for /var/run/docker.sock" 54 | sleep 0.1 55 | done 56 | echo "/var/run/docker.sock is up" 57 | fi 58 | end script 59 | -------------------------------------------------------------------------------- /salt/weave/init.sls: -------------------------------------------------------------------------------- 1 | # install and configure weave overlay network 2 | 3 | # needed for creating the bridge manually before running docker-daemon 4 | bridge-utils: 5 | pkg.installed: [] 6 | 7 | weave: 8 | file.managed: 9 | - name: /usr/local/bin/weave 10 | - source: https://github.com/weaveworks/weave/releases/download/v{{ pillar['weave']['version'] }}/weave 11 | - source_hash: {{ pillar['weave']['hash'] }} 12 | - mode: 0755 13 | service: 14 | - running 15 | - enable: True 16 | - require: 17 | - file: weave 18 | - file: weave-bridge 19 | - file: /etc/init/weave.conf 20 | - watch: 21 | - file: /usr/local/bin/weave 22 | 23 | # manually create the weave-bridge, also reboot save 24 | weave-bridge: 25 | file.blockreplace: 26 | - name: /etc/network/interfaces 27 | - marker_start: '## weave config start ##' 28 | - marker_end: '## weave config end ##' 29 | - append_if_not_found: True 30 | 31 | weave-bridge-config: 32 | file.accumulated: 33 | - filename: /etc/network/interfaces 34 | - text: | 35 | auto weave 36 | iface weave inet manual 37 | pre-up /usr/local/bin/weave --local create-bridge 38 | post-up ip a f dev weave; ip addr add dev weave {{ pillar['weave']['bridge_cidr'] }} 39 | pre-down ifconfig weave down 40 | post-down brctl delbr weave 41 | - require_in: 42 | - file: weave-bridge 43 | 44 | weave-interface: 45 | cmd.wait: 46 | - name: "ifdown weave; ifup weave;" 47 | - watch: 48 | - file: weave-bridge 49 | - require: 50 | - pkg: bridge-utils 51 | 52 | /etc/init/weave.conf: 53 | file.managed: 54 | - source: salt://weave/templates/upstart 55 | - template: jinja 56 | - require: 57 | - file: weave 58 | - file: weave-bridge 59 | - watch_in: 60 | - service: weave 61 | 62 | 63 | #include: 64 | # - .weave-scope 65 | -------------------------------------------------------------------------------- /salt/nginx/templates/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes auto; 3 | pid /var/run/nginx.pid; 4 | 5 | events { 6 | worker_connections 768; 7 | # multi_accept on; 8 | } 9 | 10 | http { 11 | 12 | ## 13 | # Basic Settings 14 | ## 15 | 16 | sendfile on; 17 | tcp_nopush on; 18 | tcp_nodelay on; 19 | keepalive_timeout 65; 20 | types_hash_max_size 2048; 21 | server_tokens off; 22 | 23 | # server_names_hash_bucket_size 64; 24 | # server_name_in_redirect off; 25 | 26 | include /etc/nginx/mime.types; 27 | default_type application/octet-stream; 28 | 29 | ## 30 | # Logging Settings 31 | ## 32 | 33 | access_log /var/log/nginx/access.log; 34 | error_log /var/log/nginx/error.log; 35 | 36 | ## 37 | # Gzip Settings 38 | ## 39 | 40 | gzip on; 41 | gzip_disable "msie6"; 42 | 43 | # gzip_vary on; 44 | # gzip_proxied any; 45 | # gzip_comp_level 6; 46 | # gzip_buffers 16 8k; 47 | # gzip_http_version 1.1; 48 | # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; 49 | 50 | ## 51 | # nginx-naxsi config 52 | ## 53 | # Uncomment it if you installed nginx-naxsi 54 | ## 55 | 56 | #include /etc/nginx/naxsi_core.rules; 57 | 58 | ## 59 | # nginx-passenger config 60 | ## 61 | # Uncomment it if you installed nginx-passenger 62 | ## 63 | 64 | #passenger_root /usr; 65 | #passenger_ruby /usr/bin/ruby; 66 | 67 | ## 68 | # Virtual Host Configs 69 | ## 70 | 71 | include /etc/nginx/conf.d/*.conf; 72 | include /etc/nginx/sites-enabled/*; 73 | } 74 | 75 | stream { 76 | include /etc/nginx/streams-enabled/*; 77 | } 78 | 79 | #mail { 80 | # # See sample authentication script at: 81 | # # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript 82 | # 83 | # # auth_http localhost/auth.php; 84 | # # pop3_capabilities "TOP" "USER"; 85 | # # imap_capabilities "IMAP4rev1" "UIDPLUS"; 86 | # 87 | # server { 88 | # listen localhost:110; 89 | # protocol pop3; 90 | # proxy on; 91 | # } 92 | # 93 | # server { 94 | # listen localhost:143; 95 | # protocol imap; 96 | # proxy on; 97 | # } 98 | #} 99 | -------------------------------------------------------------------------------- /salt/mesos-master/templates/zookeeper/zoo.cfg: -------------------------------------------------------------------------------- 1 | # http://hadoop.apache.org/zookeeper/docs/current/zookeeperAdmin.html 2 | 3 | # The number of milliseconds of each tick 4 | tickTime=2000 5 | # The number of ticks that the initial 6 | # synchronization phase can take 7 | initLimit=10 8 | # The number of ticks that can pass between 9 | # sending a request and getting an acknowledgement 10 | syncLimit=5 11 | # the directory where the snapshot is stored. 12 | dataDir=/var/lib/zookeeper 13 | # Place the dataLogDir to a separate physical disc for better performance 14 | # dataLogDir=/disk2/zookeeper 15 | 16 | # the port at which the clients will connect 17 | clientPort=2181 18 | 19 | # specify all zookeeper servers 20 | # The fist port is used by followers to connect to the leader 21 | # The second one is used for leader election 22 | {% for node, server in servers.items() %} 23 | server.{{ server.id }}={{ server.ip }}:2888:3888 24 | {% endfor %} 25 | 26 | 27 | # To avoid seeks ZooKeeper allocates space in the transaction log file in 28 | # blocks of preAllocSize kilobytes. The default block size is 64M. One reason 29 | # for changing the size of the blocks is to reduce the block size if snapshots 30 | # are taken more often. (Also, see snapCount). 31 | #preAllocSize=65536 32 | 33 | # Clients can submit requests faster than ZooKeeper can process them, 34 | # especially if there are a lot of clients. To prevent ZooKeeper from running 35 | # out of memory due to queued requests, ZooKeeper will throttle clients so that 36 | # there is no more than globalOutstandingLimit outstanding requests in the 37 | # system. The default limit is 1,000.ZooKeeper logs transactions to a 38 | # transaction log. After snapCount transactions are written to a log file a 39 | # snapshot is started and a new transaction log file is started. The default 40 | # snapCount is 10,000. 41 | #snapCount=1000 42 | 43 | # If this option is defined, requests will be will logged to a trace file named 44 | # traceFile.year.month.day. 45 | #traceFile= 46 | 47 | # Leader accepts client connections. Default value is "yes". The leader machine 48 | # coordinates updates. For higher update throughput at thes slight expense of 49 | # read throughput the leader can be configured to not accept clients and focus 50 | # on coordination. 51 | #leaderServes=yes 52 | -------------------------------------------------------------------------------- /salt/consul/init.sls: -------------------------------------------------------------------------------- 1 | # unzip must be installed to extract downloaded archives 2 | unzip: 3 | pkg.installed: [] 4 | 5 | # create a consul user which owns the /data/consul directory and runs consul 6 | consul-user: 7 | user.present: 8 | - name: consul 9 | - system: True 10 | 11 | /data: 12 | file.directory: [] 13 | 14 | /data/consul: 15 | file.directory: 16 | - user: consul 17 | - group: consul 18 | - require: 19 | - user: consul-user 20 | - file: /data 21 | 22 | # download consul and extract it to /usr/local/bin/consul 23 | consul-download: 24 | cmd.run: 25 | - name: 'curl -L https://dl.bintray.com/mitchellh/consul/{{ pillar['consul']['version'] }}_linux_amd64.zip -o /usr/src/consul-{{ pillar['consul']['version'] }}.zip' 26 | - unless: test -f /usr/src/consul-{{ pillar['consul']['version'] }}.zip 27 | 28 | consul-extract: 29 | cmd.wait: 30 | - name: 'unzip consul-{{ pillar['consul']['version'] }}.zip' 31 | - unless: test -d /usr/src/consul-{{ pillar['consul']['version'] }} 32 | - cwd: /usr/src 33 | - require: 34 | - cmd: consul-download 35 | - watch: 36 | - cmd: consul-download 37 | 38 | consul-binary: 39 | cmd.wait: 40 | - name: 'mv consul /usr/local/bin/consul && chmod +x /usr/local/bin/consul' 41 | - cwd: /usr/src 42 | - require: 43 | - cmd: consul-extract 44 | - watch: 45 | - cmd: consul-extract 46 | 47 | # download and extract consul-ui 48 | /usr/share/consul/ui: 49 | archive.extracted: 50 | - source: https://dl.bintray.com/mitchellh/consul/{{ pillar['consul']['version'] }}_web_ui.zip 51 | - source_hash: {{ pillar['consul']['hash']['ui'] }} 52 | - archive_format: zip 53 | 54 | 55 | # create upstart script for consul 56 | 57 | /etc/init/consul.conf: 58 | file.managed: 59 | - source: salt://consul/templates/upstart 60 | - watch_in: 61 | - service: consul 62 | 63 | # get number of expected nodes (all nodes with the master role) 64 | {% set expect = salt['mine.get']('G@roles:master', 'network.ip_addrs', expr_form='compound').items()|length %} 65 | #' 66 | 67 | /etc/consul/server: 68 | file.recurse: 69 | - source: salt://consul/templates/server 70 | - template: jinja 71 | - makedirs: True 72 | - watch_in: 73 | - service: consul 74 | - context: 75 | expect: {{ expect }} 76 | 77 | 78 | consul: 79 | service: 80 | - running 81 | require: 82 | - file: /etc/init/consul.conf 83 | - archive: /usr/share/consul/ui 84 | watch: 85 | - file: /etc/inid/consul.conf 86 | 87 | 88 | # dnsmasq config 89 | dnsmasq: 90 | pkg.installed: [] 91 | service: 92 | - running 93 | 94 | 95 | /etc/dnsmasq.d/10-consul: 96 | file.managed: 97 | - source: salt://consul/templates/dnsmasq 98 | - template: jinja 99 | - require: 100 | - pkg: dnsmasq 101 | - watch_in: 102 | - service: dnsmasq 103 | 104 | 105 | # consul template 106 | /usr/src/consul-template: 107 | archive.extracted: 108 | - source: https://github.com/hashicorp/consul-template/releases/download/v{{ pillar['consul']['template']['version'] }}/consul-template_{{ pillar['consul']['template']['version'] }}_linux_amd64.tar.gz 109 | - source_hash: {{ pillar['consul']['template']['hash'] }} 110 | - archive_format: tar 111 | - if_missing: /usr/src/consul-template/consul-template_{{ pillar['consul']['template']['version'] }}_linux_amd64 112 | 113 | /usr/local/bin/consul-template: 114 | file.copy: 115 | - source: /usr/src/consul-template/consul-template_{{ pillar['consul']['template']['version'] }}_linux_amd64/consul-template 116 | - mode: 0755 117 | - force: True 118 | - require: 119 | - archive: /usr/src/consul-template 120 | - watch: 121 | - archive: /usr/src/consul-template 122 | 123 | # templates for consul-template 124 | /etc/consul/template.hcl: 125 | file.managed: 126 | - source: salt://consul/templates/template.hcl 127 | - template: jinja 128 | - watch_in: 129 | - service: consul-template 130 | 131 | # templates with customer_id = 0 for global services 132 | /etc/consul/templates/global: 133 | file.recurse: 134 | - source: salt://consul/templates/templates 135 | - makedirs: True 136 | - template: jinja 137 | - watch_in: 138 | - service: consul-template 139 | - require_in: 140 | - service: consul-template 141 | - context: 142 | customer_id: 0 143 | 144 | # customer specific templates 145 | {% if pillar['schub']['customer_id'] != 0 %} 146 | /etc/consul/templates/customer: 147 | file.recurse: 148 | - source: salt://consul/templates/templates 149 | - makedirs: True 150 | - template: jinja 151 | - watch_in: 152 | - service: consul-template 153 | - require_in: 154 | - service: consul-template 155 | - context: 156 | customer_id: {{ pillar['schub']['customer_id'] }} 157 | 158 | {% else %} 159 | /etc/nginx/sites-enabled/service-proxy: 160 | file.absent: [] 161 | 162 | /etc/nginx/sites-enabled/web-proxy: 163 | file.absent: [] 164 | 165 | /etc/nginx/streams-enabled/service-proxy: 166 | file.absent: [] 167 | 168 | {% endif %} 169 | 170 | # upstart for consul-template 171 | /etc/init/consul-template.conf: 172 | file.managed: 173 | - source: salt://consul/templates/template-upstart 174 | - watch_in: 175 | - service: consul-template 176 | 177 | consul-template: 178 | service: 179 | - running 180 | - enable: True 181 | - require: 182 | - file: /etc/init/consul-template.conf 183 | - file: /etc/consul/template.hcl 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This repository is used to install a working Mesos-Cluster with Marathon and some extra tools for using in production. SaltStack will be used to perform the installation. So one node will be a SaltStack master, all other nodes will be minions. There are going to be two different roles: A master role (must not be the SaltStack master) for performing cluster actions, a slave role for running Docker containers. 4 | 5 | ![Architecture overview](architecture.png) 6 | 7 | The following software will be installed: 8 | 9 | Role Master: 10 | 11 | - [Zookeeper](https://open.mesosphere.com/getting-started/datacenter/install/) 12 | - [Mesos-Master](https://open.mesosphere.com/getting-started/datacenter/install/) 13 | - [Marathon](https://open.mesosphere.com/getting-started/datacenter/install/) 14 | - [Consul](https://www.consul.io/) 15 | 16 | Role Slave: 17 | 18 | - [Mesos-Slave](https://open.mesosphere.com/getting-started/datacenter/install/) 19 | - [Docker](https://www.docker.com/) 20 | - [cAdvisor](https://github.com/google/cadvisor) for exporting metrics to prometheus ([marmelatze/cadvisor](https://registry.hub.docker.com/u/marmelatze/cadvisor/) is used until https://github.com/google/cadvisor/pull/780 is merged) 21 | - [Registrator](https://github.com/gliderlabs/registrator) for registering services with Consul. ([Marmelatze/registrator](https://github.com/Marmelatze/registrator) is used until https://github.com/gliderlabs/registrator/pull/149 is merged) 22 | - [Weave](http://weave.works/) for providing a overlay network between the containers 23 | 24 | 25 | # Setup 26 | 27 | First you will need a Salt-Master to coordinate all Salt-Minions. Then you need a an odd number of master servers (3, 5, 7 ...). One of them can also be used as Salt-Master. The other master servers will be minions then. 28 | 29 | 30 | ## Salt-Master-Setup: 31 | 32 | Combined setup for master and minion: 33 | 34 | ``` 35 | curl -L https://bootstrap.saltstack.com -o install_salt.sh 36 | sudo sh install_salt.sh -U -M -P -A localhost 37 | ``` 38 | 39 | Clone this repository to /srv/salt: 40 | 41 | ``` 42 | sudo git clone https://github.com/Marmelatze/saltstack-mesos-test /srv/salt 43 | ``` 44 | 45 | Change the config of the Salt-Master: 46 | 47 | ```yaml 48 | #/etc/salt/master 49 | 50 | file_roots: 51 | base: 52 | - /srv/salt/salt 53 | # ... 54 | pillar_roots: 55 | base: 56 | - /srv/salt/pillar 57 | ``` 58 | 59 | Restart the master: 60 | 61 | ``` 62 | sudo service salt-master restart 63 | ``` 64 | 65 | You also have to change the minion config as described in the next section. The minion setup can be skipped, as it was already done previously. 66 | 67 | ## Minion-Setup 68 | 69 | Nur den Minion installieren ohne Master (nicht nötig, wenn Master installiert wurde): 70 | 71 | Install the Minion: 72 | 73 | ``` 74 | curl -L https://bootstrap.saltstack.com -o install_salt.sh 75 | sudo sh install_salt.sh -U -A IP_MASTERS 76 | ``` 77 | 78 | Add the following to the end of the minion config: 79 | 80 | ```yaml 81 | # /etc/salt/minion 82 | 83 | # ... 84 | mine_interval: 5 85 | mine_functions: 86 | network.ip_addrs: 87 | interface: eth0 88 | zookeeper: 89 | - mine_function: pillar.get 90 | - zookeeper 91 | ``` 92 | 93 | Change the salt-grains. You have to select a numerical ID for each node (starting from 1). The `customer_id` can be ommitted if not needed. It will add a attribute to the mesos slave, so you can constraint an application to slaves of this customer. The combination of `host_id` and `customer_id` need to be unique and it will be used as IP subnet of the node (e.g. 10.3.2.0/24 for host_id=2 and customer_id=3). 94 | 95 | ``` 96 | # /etc/salt/grains 97 | 98 | # Customer-Id this host is assigned to (numeric)- 99 | customer_id: 0 100 | # ID of this host. 101 | host_id: ID 102 | 103 | # ID for zookeeper, only needed for masters. 104 | zk_id: ID 105 | 106 | # Available roles are master and slave. A node can use both. 107 | roles: 108 | - master 109 | - slave 110 | ``` 111 | 112 | Restart the minion: 113 | 114 | ``` 115 | sudo service salt-minion restart 116 | ``` 117 | 118 | SaltStack uses a public-key authentication, so you need to accept the newly created minion on the master. 119 | 120 | ``` 121 | sudo salt-key -A 122 | ``` 123 | 124 | ## Run SaltStack 125 | 126 | After the Minions have been setup run SaltStack. 127 | 128 | ``` 129 | sudo salt '*' state.highstate 130 | ``` 131 | 132 | 133 | # Monitoring 134 | 135 | Monitoring is done with Prometheus and Grafana. With the sample config the Grafana settings are stored in a MySQL database. So you have to setup a MySQL database or delete the corresponding settings from monitoring/marathon.json. 136 | 137 | In order to make MySQL available in the consul service discovery you need to add a service config to consul. It is recommended to install a consul agent to the same server and join it to the cluster (see [docs](https://www.consul.io/docs/agent/services.html) for more details and options). 138 | 139 | ```json 140 | # /etc/consul/server/mysql.json 141 | 142 | { 143 | "service": { 144 | "name": "mysql", 145 | "port": 3306, 146 | "tags": ["customer-0"] 147 | } 148 | } 149 | ``` 150 | 151 | The tag `customer-0` is used to distinguish global services from customer services. Customer 0 is the global customer and his services are available on all nodes. 152 | 153 | Create a grafana database and a user: 154 | 155 | ```sql 156 | CREATE DATABASE grafana; 157 | CREATE USER 'grafana'@'%' IDENTIFIED BY 'grafana'; 158 | GRANT ALL PRIVILEGES ON grafana.* TO 'grafana'@'%'; 159 | FLUSH PRIVILEGES; 160 | ``` 161 | 162 | To add monitoring services (Prometheus, Grafana) execute the following on the salt-master assuming you cloned the repository to /srv/salt. This will add the prometheus config to consul and create a marathon group: 163 | 164 | ```bash 165 | cd /srv/salt/monitoring 166 | CONSUL_HOST=http://localhost:8500 167 | MARATHON_HOST=http://localhost:8080 168 | curl -X PUT -s --data-binary "@config_prometheus.yml" $CONSUL_HOST/v1/kv/config/prometheus 169 | curl -X PUT -s --data-binary "@proxy_mysql.json" $CONSUL_HOST/v1/kv/proxy/tcp/mysql 170 | curl -X PUT -s --data-binary "@proxy_prometheus.json" $CONSUL_HOSTv1/kv/proxy/web/prometheus 171 | curl -X PUT -s --data-binary "@proxy_grafana.json" $CONSUL_HOST/v1/kv/proxy/web/grafana 172 | curl -X PUT -s -H "Content-Type:application/json" --data "@marathon.json" $MARATHON_HOST/v2/groups/monitoring 173 | ``` 174 | 175 | Prometheus will be accessible at port 9090 and the Grafana dashboard at port 3000. The default credentials for grafana are (admin/install). You will have to create a Prometheus data source in Grafana first. 176 | 177 | Configure it like the following: 178 | 179 | * Name: Prometheus 180 | * Type: Prometheus 181 | * Default: true 182 | * URL: `http://gateway:9090` 183 | * Access: proxy 184 | * Basic-Auth: false 185 | 186 | A example dashboard is located at [monitoring/dashboard.json](monitoring/dashboard.json) and can be imported in Grafana. 187 | 188 | # Service Gateway 189 | 190 | The service gateway provides access from containers to services provided by other containers or external services. The configuration is managed in the key value storage from consul. Changes will be pulled by [Consul-Template](https://github.com/hashicorp/consul-template) and will form a nginx config. Each service gets a separate config file at `/proxy//` (See [here](salt/consul/templates/templates) for the templates). There are three proxy types: 191 | 192 | * HTTP reverse proxy (located in `/proxy/http/`): This type will redirect all incomming traffic on a specific port to the service. Configuration: 193 | 194 | ```json 195 | { 196 | "service": "grafana", 197 | "servicePort": 3000, 198 | "containerPort": 3000, 199 | "timeout": 60, 200 | "external": true 201 | } 202 | ``` 203 | 204 | * `service`: Name of the service, as its stored in consul. 205 | * `containerPort`: The port used in the container. 206 | * `servicePort`: The port exposed by the service gateway. 207 | * `external` (Optional): Whether this service can be access from the outside or only by other containers. (Default: `false`) 208 | * `timeout` (Optional): When there is no response from the service within the given timeout, nginx will yield a 504 gateway timeout. 209 | 210 | * Web reverse proxy (located in `/proxy/web/`): Like the HTTP reverse proxy, but all services will use port 80 and be distinguished by the domain (e.g. `grafana.test.schub.local`) 211 | 212 | ```json 213 | { 214 | "service": "grafana", 215 | "containerPort": 3000, 216 | "domain": "dashboard", 217 | "timeout": 60, 218 | "external": true 219 | } 220 | ``` 221 | 222 | * `service`: Name of the service, as its stored in consul. 223 | * `containerPort`: The port used in the container. 224 | * `domain`: The subdomain for this service. 225 | * `external` (Optional): Whether this service can be access from the outside or only by other containers. (Default: `false`) 226 | * `timeout` (Optional): When there is no response from the service within the given timeout, nginx will yield a 504 gateway timeout. 227 | 228 | * TCP reverse proxy (located in `/proxy/tcp/`): Like the HTTP reverse proxy, but can be used for any TCP based service. 229 | 230 | ```json 231 | { 232 | "service": "mysql", 233 | "servicePort": 3306, 234 | "containerPort": 3306, 235 | "external": false 236 | } 237 | ``` 238 | 239 | * `service`: Name of the service, as its stored in consul. 240 | * `containerPort`: The port used in the container. 241 | * `servicePort`: The port exposed by the service gateway. 242 | * `external` (Optional): Whether this service can be access from the outside or only by other containers. (Default: `false`) 243 | 244 | 245 | # Scaling Marathon Apps 246 | 247 | Automatic scaling of marathon apps is done via https://github.com/Marmelatze/docker-controller. 248 | To use it the marathon-scaler app must be run as marathon app itself: 249 | 250 | ```bash 251 | cd /srv/salt/scaling 252 | MARATHON_HOST=http://localhost:8080 253 | curl -X PUT -s -H "Content-Type:application/json" --data "@marathon-scaler.json" $MARATHON_HOST/v2/apps/scaling 254 | ``` 255 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Florian Pfitzer 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | Apache License 16 | Version 2.0, January 2004 17 | http://www.apache.org/licenses/ 18 | 19 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 20 | 21 | 1. Definitions. 22 | 23 | "License" shall mean the terms and conditions for use, reproduction, 24 | and distribution as defined by Sections 1 through 9 of this document. 25 | 26 | "Licensor" shall mean the copyright owner or entity authorized by 27 | the copyright owner that is granting the License. 28 | 29 | "Legal Entity" shall mean the union of the acting entity and all 30 | other entities that control, are controlled by, or are under common 31 | control with that entity. For the purposes of this definition, 32 | "control" means (i) the power, direct or indirect, to cause the 33 | direction or management of such entity, whether by contract or 34 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 35 | outstanding shares, or (iii) beneficial ownership of such entity. 36 | 37 | "You" (or "Your") shall mean an individual or Legal Entity 38 | exercising permissions granted by this License. 39 | 40 | "Source" form shall mean the preferred form for making modifications, 41 | including but not limited to software source code, documentation 42 | source, and configuration files. 43 | 44 | "Object" form shall mean any form resulting from mechanical 45 | transformation or translation of a Source form, including but 46 | not limited to compiled object code, generated documentation, 47 | and conversions to other media types. 48 | 49 | "Work" shall mean the work of authorship, whether in Source or 50 | Object form, made available under the License, as indicated by a 51 | copyright notice that is included in or attached to the work 52 | (an example is provided in the Appendix below). 53 | 54 | "Derivative Works" shall mean any work, whether in Source or Object 55 | form, that is based on (or derived from) the Work and for which the 56 | editorial revisions, annotations, elaborations, or other modifications 57 | represent, as a whole, an original work of authorship. For the purposes 58 | of this License, Derivative Works shall not include works that remain 59 | separable from, or merely link (or bind by name) to the interfaces of, 60 | the Work and Derivative Works thereof. 61 | 62 | "Contribution" shall mean any work of authorship, including 63 | the original version of the Work and any modifications or additions 64 | to that Work or Derivative Works thereof, that is intentionally 65 | submitted to Licensor for inclusion in the Work by the copyright owner 66 | or by an individual or Legal Entity authorized to submit on behalf of 67 | the copyright owner. For the purposes of this definition, "submitted" 68 | means any form of electronic, verbal, or written communication sent 69 | to the Licensor or its representatives, including but not limited to 70 | communication on electronic mailing lists, source code control systems, 71 | and issue tracking systems that are managed by, or on behalf of, the 72 | Licensor for the purpose of discussing and improving the Work, but 73 | excluding communication that is conspicuously marked or otherwise 74 | designated in writing by the copyright owner as "Not a Contribution." 75 | 76 | "Contributor" shall mean Licensor and any individual or Legal Entity 77 | on behalf of whom a Contribution has been received by Licensor and 78 | subsequently incorporated within the Work. 79 | 80 | 2. Grant of Copyright License. Subject to the terms and conditions of 81 | this License, each Contributor hereby grants to You a perpetual, 82 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 83 | copyright license to reproduce, prepare Derivative Works of, 84 | publicly display, publicly perform, sublicense, and distribute the 85 | Work and such Derivative Works in Source or Object form. 86 | 87 | 3. Grant of Patent License. Subject to the terms and conditions of 88 | this License, each Contributor hereby grants to You a perpetual, 89 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 90 | (except as stated in this section) patent license to make, have made, 91 | use, offer to sell, sell, import, and otherwise transfer the Work, 92 | where such license applies only to those patent claims licensable 93 | by such Contributor that are necessarily infringed by their 94 | Contribution(s) alone or by combination of their Contribution(s) 95 | with the Work to which such Contribution(s) was submitted. If You 96 | institute patent litigation against any entity (including a 97 | cross-claim or counterclaim in a lawsuit) alleging that the Work 98 | or a Contribution incorporated within the Work constitutes direct 99 | or contributory patent infringement, then any patent licenses 100 | granted to You under this License for that Work shall terminate 101 | as of the date such litigation is filed. 102 | 103 | 4. Redistribution. You may reproduce and distribute copies of the 104 | Work or Derivative Works thereof in any medium, with or without 105 | modifications, and in Source or Object form, provided that You 106 | meet the following conditions: 107 | 108 | (a) You must give any other recipients of the Work or 109 | Derivative Works a copy of this License; and 110 | 111 | (b) You must cause any modified files to carry prominent notices 112 | stating that You changed the files; and 113 | 114 | (c) You must retain, in the Source form of any Derivative Works 115 | that You distribute, all copyright, patent, trademark, and 116 | attribution notices from the Source form of the Work, 117 | excluding those notices that do not pertain to any part of 118 | the Derivative Works; and 119 | 120 | (d) If the Work includes a "NOTICE" text file as part of its 121 | distribution, then any Derivative Works that You distribute must 122 | include a readable copy of the attribution notices contained 123 | within such NOTICE file, excluding those notices that do not 124 | pertain to any part of the Derivative Works, in at least one 125 | of the following places: within a NOTICE text file distributed 126 | as part of the Derivative Works; within the Source form or 127 | documentation, if provided along with the Derivative Works; or, 128 | within a display generated by the Derivative Works, if and 129 | wherever such third-party notices normally appear. The contents 130 | of the NOTICE file are for informational purposes only and 131 | do not modify the License. You may add Your own attribution 132 | notices within Derivative Works that You distribute, alongside 133 | or as an addendum to the NOTICE text from the Work, provided 134 | that such additional attribution notices cannot be construed 135 | as modifying the License. 136 | 137 | You may add Your own copyright statement to Your modifications and 138 | may provide additional or different license terms and conditions 139 | for use, reproduction, or distribution of Your modifications, or 140 | for any such Derivative Works as a whole, provided Your use, 141 | reproduction, and distribution of the Work otherwise complies with 142 | the conditions stated in this License. 143 | 144 | 5. Submission of Contributions. Unless You explicitly state otherwise, 145 | any Contribution intentionally submitted for inclusion in the Work 146 | by You to the Licensor shall be under the terms and conditions of 147 | this License, without any additional terms or conditions. 148 | Notwithstanding the above, nothing herein shall supersede or modify 149 | the terms of any separate license agreement you may have executed 150 | with Licensor regarding such Contributions. 151 | 152 | 6. Trademarks. This License does not grant permission to use the trade 153 | names, trademarks, service marks, or product names of the Licensor, 154 | except as required for reasonable and customary use in describing the 155 | origin of the Work and reproducing the content of the NOTICE file. 156 | 157 | 7. Disclaimer of Warranty. Unless required by applicable law or 158 | agreed to in writing, Licensor provides the Work (and each 159 | Contributor provides its Contributions) on an "AS IS" BASIS, 160 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 161 | implied, including, without limitation, any warranties or conditions 162 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 163 | PARTICULAR PURPOSE. You are solely responsible for determining the 164 | appropriateness of using or redistributing the Work and assume any 165 | risks associated with Your exercise of permissions under this License. 166 | 167 | 8. Limitation of Liability. In no event and under no legal theory, 168 | whether in tort (including negligence), contract, or otherwise, 169 | unless required by applicable law (such as deliberate and grossly 170 | negligent acts) or agreed to in writing, shall any Contributor be 171 | liable to You for damages, including any direct, indirect, special, 172 | incidental, or consequential damages of any character arising as a 173 | result of this License or out of the use or inability to use the 174 | Work (including but not limited to damages for loss of goodwill, 175 | work stoppage, computer failure or malfunction, or any and all 176 | other commercial damages or losses), even if such Contributor 177 | has been advised of the possibility of such damages. 178 | 179 | 9. Accepting Warranty or Additional Liability. While redistributing 180 | the Work or Derivative Works thereof, You may choose to offer, 181 | and charge a fee for, acceptance of support, warranty, indemnity, 182 | or other liability obligations and/or rights consistent with this 183 | License. However, in accepting such obligations, You may act only 184 | on Your own behalf and on Your sole responsibility, not on behalf 185 | of any other Contributor, and only if You agree to indemnify, 186 | defend, and hold each Contributor harmless for any liability 187 | incurred by, or claims asserted against, such Contributor by reason 188 | of your accepting any such warranty or additional liability. 189 | 190 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /monitoring/dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "title": "Containers", 4 | "originalTitle": "Containers", 5 | "tags": [], 6 | "style": "dark", 7 | "timezone": "browser", 8 | "editable": true, 9 | "hideControls": false, 10 | "sharedCrosshair": true, 11 | "rows": [ 12 | { 13 | "collapse": false, 14 | "editable": true, 15 | "height": "250px", 16 | "panels": [ 17 | { 18 | "cacheTimeout": null, 19 | "colorBackground": false, 20 | "colorValue": false, 21 | "colors": [ 22 | "rgba(245, 54, 54, 0.9)", 23 | "rgba(237, 129, 40, 0.89)", 24 | "rgba(50, 172, 45, 0.97)" 25 | ], 26 | "editable": true, 27 | "error": false, 28 | "format": "none", 29 | "id": 6, 30 | "interval": null, 31 | "links": [], 32 | "maxDataPoints": 100, 33 | "nullPointMode": "connected", 34 | "nullText": null, 35 | "postfix": "", 36 | "postfixFontSize": "50%", 37 | "prefix": "", 38 | "prefixFontSize": "110%", 39 | "span": 2, 40 | "sparkline": { 41 | "fillColor": "rgba(31, 118, 189, 0.18)", 42 | "full": false, 43 | "lineColor": "rgb(31, 120, 193)", 44 | "show": true 45 | }, 46 | "targets": [ 47 | { 48 | "calculatedInterval": "1m", 49 | "datasourceErrors": {}, 50 | "errors": {}, 51 | "expr": "count(container_memory_usage_bytes{id=~\"/docker/\",node=~\"$node\",name=~\"$container\",marathon_app_id =~\"$marathon_app\",image=~\"$image\",customer=~\"$customer\"})", 52 | "interval": "", 53 | "intervalFactor": 10, 54 | "legendFormat": "", 55 | "metric": "", 56 | "prometheusLink": "/api/datasources/proxy/1/graph#%5B%7B%22expr%22%3A%22count(container_memory_usage_bytes%7Bid%3D~%5C%22%2Fdocker%2F%5C%22%2Cnode%3D~%5C%22%24node%5C%22%2Cname%3D~%5C%22%24container%5C%22%2Cmarathon_app_id%20%3D~%5C%22%24marathon_app%5C%22%2Cimage%3D~%5C%22%24image%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D)%22%2C%22range_input%22%3A%22901s%22%2C%22end_input%22%3A%222015-8-2%2011%3A8%22%2C%22step_input%22%3A%22%22%2C%22tab%22%3A0%7D%5D" 57 | } 58 | ], 59 | "thresholds": "", 60 | "title": "Running Containers", 61 | "type": "singlestat", 62 | "valueFontSize": "170%", 63 | "valueMaps": [ 64 | { 65 | "op": "=", 66 | "text": "N/A", 67 | "value": "null" 68 | } 69 | ], 70 | "valueName": "current" 71 | }, 72 | { 73 | "aliasColors": {}, 74 | "bars": false, 75 | "datasource": null, 76 | "decimals": 0, 77 | "editable": true, 78 | "error": false, 79 | "fill": 1, 80 | "grid": { 81 | "leftLogBase": 1, 82 | "leftMax": null, 83 | "leftMin": 0, 84 | "rightLogBase": 1, 85 | "rightMax": null, 86 | "rightMin": null, 87 | "threshold1": null, 88 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 89 | "threshold2": null, 90 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 91 | }, 92 | "id": 4, 93 | "legend": { 94 | "alignAsTable": true, 95 | "avg": false, 96 | "current": true, 97 | "max": false, 98 | "min": false, 99 | "rightSide": true, 100 | "show": true, 101 | "total": false, 102 | "values": true 103 | }, 104 | "lines": true, 105 | "linewidth": 2, 106 | "links": [], 107 | "nullPointMode": "connected", 108 | "percentage": false, 109 | "pointradius": 5, 110 | "points": false, 111 | "renderer": "flot", 112 | "seriesOverrides": [], 113 | "span": 5, 114 | "stack": false, 115 | "steppedLine": false, 116 | "targets": [ 117 | { 118 | "calculatedInterval": "10s", 119 | "datasourceErrors": {}, 120 | "errors": {}, 121 | "expr": "count(container_memory_usage_bytes{id=~\"/docker/\",node=~\"$node\",name=~\"$container\",marathon_app_id =~\"$marathon_app\",image=~\"$image\",customer=~\"$customer\"}) by (node)", 122 | "hide": false, 123 | "interval": "", 124 | "intervalFactor": 10, 125 | "legendFormat": "{{node}}", 126 | "metric": "", 127 | "prometheusLink": "/api/datasources/proxy/1/graph#%5B%7B%22expr%22%3A%22count(container_memory_usage_bytes%7Bid%3D~%5C%22%2Fdocker%2F%5C%22%2Cnode%3D~%5C%22%24node%5C%22%2Cname%3D~%5C%22%24container%5C%22%2Cmarathon_app_id%20%3D~%5C%22%24marathon_app%5C%22%2Cimage%3D~%5C%22%24image%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D)%20by%20(node)%22%2C%22range_input%22%3A%22900s%22%2C%22end_input%22%3A%222015-8-2%2011%3A8%22%2C%22step_input%22%3A%22%22%2C%22stacked%22%3Afalse%2C%22tab%22%3A0%7D%5D" 128 | } 129 | ], 130 | "timeFrom": null, 131 | "timeShift": null, 132 | "title": "Running Containers", 133 | "tooltip": { 134 | "shared": true, 135 | "value_type": "cumulative" 136 | }, 137 | "type": "graph", 138 | "x-axis": true, 139 | "y-axis": true, 140 | "y_formats": [ 141 | "short", 142 | "short" 143 | ] 144 | }, 145 | { 146 | "aliasColors": {}, 147 | "bars": false, 148 | "datasource": null, 149 | "decimals": null, 150 | "editable": true, 151 | "error": false, 152 | "fill": 1, 153 | "grid": { 154 | "leftLogBase": 1, 155 | "leftMax": 100, 156 | "leftMin": 0, 157 | "rightLogBase": 1, 158 | "rightMax": null, 159 | "rightMin": null, 160 | "threshold1": 80, 161 | "threshold1Color": "rgba(216, 200, 27, 0.47)", 162 | "threshold2": 90, 163 | "threshold2Color": "rgba(234, 112, 112, 0.22)", 164 | "thresholdLine": false 165 | }, 166 | "id": 9, 167 | "legend": { 168 | "avg": false, 169 | "current": false, 170 | "max": false, 171 | "min": false, 172 | "show": true, 173 | "total": false, 174 | "values": false 175 | }, 176 | "lines": true, 177 | "linewidth": 2, 178 | "links": [], 179 | "nullPointMode": "connected", 180 | "percentage": false, 181 | "pointradius": 5, 182 | "points": false, 183 | "renderer": "flot", 184 | "seriesOverrides": [], 185 | "span": 5, 186 | "stack": false, 187 | "steppedLine": false, 188 | "targets": [ 189 | { 190 | "calculatedInterval": "3m", 191 | "datasourceErrors": {}, 192 | "errors": {}, 193 | "expr": "sum(container_fs_usage_bytes{node=~\"$node\",customer=~\"$customer\"}) by (node) / sum(container_fs_limit_bytes{node=~\"$node\",customer=~\"$customer\"}) by (node) * 100", 194 | "interval": "", 195 | "intervalFactor": 3, 196 | "legendFormat": "{{ node }}", 197 | "metric": "", 198 | "prometheusLink": "/api/datasources/proxy/1/graph#%5B%7B%22expr%22%3A%22sum(container_fs_usage_bytes%7Bnode%3D~%5C%22%24node%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D)%20by%20(node)%20%2F%20sum(container_fs_limit_bytes%7Bnode%3D~%5C%22%24node%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D)%20by%20(node)%20*%20100%22%2C%22range_input%22%3A%2286400s%22%2C%22end_input%22%3A%222015-8-2%2011%3A9%22%2C%22step_input%22%3A%22%22%2C%22stacked%22%3Afalse%2C%22tab%22%3A0%7D%5D" 199 | } 200 | ], 201 | "timeFrom": "1d", 202 | "timeShift": null, 203 | "title": "Disk Usage", 204 | "tooltip": { 205 | "shared": true, 206 | "value_type": "cumulative" 207 | }, 208 | "type": "graph", 209 | "x-axis": true, 210 | "y-axis": true, 211 | "y_formats": [ 212 | "percent", 213 | "short" 214 | ] 215 | } 216 | ], 217 | "title": "Row" 218 | }, 219 | { 220 | "collapse": false, 221 | "editable": true, 222 | "height": "250px", 223 | "panels": [ 224 | { 225 | "aliasColors": {}, 226 | "bars": false, 227 | "datasource": null, 228 | "editable": true, 229 | "error": false, 230 | "fill": 1, 231 | "grid": { 232 | "leftLogBase": 1, 233 | "leftMax": 100, 234 | "leftMin": 0, 235 | "rightLogBase": 1, 236 | "rightMax": null, 237 | "rightMin": null, 238 | "threshold1": null, 239 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 240 | "threshold2": null, 241 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 242 | }, 243 | "id": 7, 244 | "legend": { 245 | "avg": true, 246 | "current": false, 247 | "max": true, 248 | "min": true, 249 | "show": true, 250 | "total": false, 251 | "values": true 252 | }, 253 | "lines": true, 254 | "linewidth": 2, 255 | "links": [], 256 | "nullPointMode": "connected", 257 | "percentage": false, 258 | "pointradius": 5, 259 | "points": false, 260 | "renderer": "flot", 261 | "seriesOverrides": [], 262 | "span": 3, 263 | "stack": false, 264 | "steppedLine": false, 265 | "targets": [ 266 | { 267 | "calculatedInterval": "10s", 268 | "datasourceErrors": {}, 269 | "errors": {}, 270 | "expr": "sum(rate(container_cpu_usage_seconds_total{id=\"/\",node=~\"$node\",customer=~\"$customer\"}[30s])) / count(container_cpu_usage_seconds_total{id=\"/\",customer=~\"$customer\"}) * 100", 271 | "interval": "", 272 | "intervalFactor": 10, 273 | "metric": "", 274 | "prometheusLink": "/api/datasources/proxy/1/graph#%5B%7B%22expr%22%3A%22sum(rate(container_cpu_usage_seconds_total%7Bid%3D%5C%22%2F%5C%22%2Cnode%3D~%5C%22%24node%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D%5B30s%5D))%20%2F%20count(container_cpu_usage_seconds_total%7Bid%3D%5C%22%2F%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D)%20*%20100%22%2C%22range_input%22%3A%22900s%22%2C%22end_input%22%3A%222015-8-2%2011%3A12%22%2C%22step_input%22%3A%22%22%2C%22stacked%22%3Afalse%2C%22tab%22%3A0%7D%5D", 275 | "legendFormat": "CPU Usage" 276 | } 277 | ], 278 | "timeFrom": null, 279 | "timeShift": null, 280 | "title": "CPU Total", 281 | "tooltip": { 282 | "shared": true, 283 | "value_type": "cumulative" 284 | }, 285 | "type": "graph", 286 | "x-axis": true, 287 | "y-axis": true, 288 | "y_formats": [ 289 | "percent", 290 | "short" 291 | ] 292 | }, 293 | { 294 | "aliasColors": {}, 295 | "bars": false, 296 | "datasource": "Prometheus", 297 | "editable": true, 298 | "error": false, 299 | "fill": 1, 300 | "grid": { 301 | "leftLogBase": 1, 302 | "leftMax": null, 303 | "leftMin": null, 304 | "rightLogBase": 1, 305 | "rightMax": null, 306 | "rightMin": null, 307 | "threshold1": null, 308 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 309 | "threshold2": null, 310 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 311 | }, 312 | "height": "300", 313 | "hideTimeOverride": false, 314 | "id": 1, 315 | "legend": { 316 | "avg": true, 317 | "current": false, 318 | "hideEmpty": false, 319 | "max": false, 320 | "min": false, 321 | "rightSide": false, 322 | "show": true, 323 | "total": false, 324 | "values": true 325 | }, 326 | "lines": true, 327 | "linewidth": 2, 328 | "links": [], 329 | "nullPointMode": "null as zero", 330 | "percentage": false, 331 | "pointradius": 1, 332 | "points": false, 333 | "renderer": "flot", 334 | "seriesOverrides": [], 335 | "span": 9, 336 | "stack": false, 337 | "steppedLine": false, 338 | "targets": [ 339 | { 340 | "calculatedInterval": "5s", 341 | "datasourceErrors": {}, 342 | "errors": {}, 343 | "expr": "topk(5, sum(rate(container_cpu_usage_seconds_total{id=~\"^/docker/\",name=~\"$container\",node=~\"$node\",marathon_app_id=~\"$marathon_app\",image=~\"$image\",customer=~\"$customer\"}[30s])) by (name, node, marathon_app_id)) * 100", 344 | "interval": "", 345 | "intervalFactor": 5, 346 | "legendFormat": "{{ (typeof marathon_app_id != \"undefined\" ? marathon_app_id : name) }} on {{ node }}", 347 | "metric": "", 348 | "prometheusLink": "/api/datasources/proxy/1/graph#%5B%7B%22expr%22%3A%22topk(5%2C%20sum(rate(container_cpu_usage_seconds_total%7Bid%3D~%5C%22%5E%2Fdocker%2F%5C%22%2Cname%3D~%5C%22%24container%5C%22%2Cnode%3D~%5C%22%24node%5C%22%2Cmarathon_app_id%3D~%5C%22%24marathon_app%5C%22%2Cimage%3D~%5C%22%24image%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D%5B30s%5D))%20by%20(name%2C%20node%2C%20marathon_app_id))%20*%20100%22%2C%22range_input%22%3A%22900s%22%2C%22end_input%22%3A%222015-8-2%2011%3A10%22%2C%22step_input%22%3A%22%22%2C%22stacked%22%3Afalse%2C%22tab%22%3A0%7D%5D" 349 | } 350 | ], 351 | "timeFrom": null, 352 | "timeShift": null, 353 | "title": "Container CPU", 354 | "tooltip": { 355 | "shared": true, 356 | "value_type": "cumulative" 357 | }, 358 | "type": "graph", 359 | "x-axis": true, 360 | "y-axis": true, 361 | "y_formats": [ 362 | "percent", 363 | "short" 364 | ] 365 | } 366 | ], 367 | "showTitle": false, 368 | "title": "New row" 369 | }, 370 | { 371 | "collapse": false, 372 | "editable": true, 373 | "height": "250px", 374 | "panels": [ 375 | { 376 | "aliasColors": {}, 377 | "bars": false, 378 | "datasource": null, 379 | "decimals": 2, 380 | "editable": true, 381 | "error": false, 382 | "fill": 1, 383 | "grid": { 384 | "leftLogBase": 1, 385 | "leftMax": null, 386 | "leftMin": 0, 387 | "rightLogBase": 1, 388 | "rightMax": null, 389 | "rightMin": null, 390 | "threshold1": null, 391 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 392 | "threshold2": null, 393 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 394 | }, 395 | "id": 8, 396 | "legend": { 397 | "avg": true, 398 | "current": false, 399 | "max": true, 400 | "min": true, 401 | "show": true, 402 | "total": false, 403 | "values": true 404 | }, 405 | "lines": true, 406 | "linewidth": 2, 407 | "links": [], 408 | "nullPointMode": "connected", 409 | "percentage": false, 410 | "pointradius": 5, 411 | "points": false, 412 | "renderer": "flot", 413 | "seriesOverrides": [], 414 | "span": 3, 415 | "stack": false, 416 | "steppedLine": false, 417 | "targets": [ 418 | { 419 | "calculatedInterval": "10s", 420 | "datasourceErrors": {}, 421 | "errors": {}, 422 | "expr": "sum(container_memory_usage_bytes{id=\"/\",node=~\"$node\",customer=~\"$customer\"})", 423 | "intervalFactor": 10, 424 | "metric": "", 425 | "prometheusLink": "/api/datasources/proxy/1/graph#%5B%7B%22expr%22%3A%22sum(container_memory_usage_bytes%7Bid%3D%5C%22%2F%5C%22%2Cnode%3D~%5C%22%24node%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D)%22%2C%22range_input%22%3A%22900s%22%2C%22end_input%22%3A%222015-8-2%2011%3A12%22%2C%22step_input%22%3A%22%22%2C%22stacked%22%3Afalse%2C%22tab%22%3A0%7D%5D", 426 | "legendFormat": "Memory Usage" 427 | } 428 | ], 429 | "timeFrom": null, 430 | "timeShift": null, 431 | "title": "Memory Total", 432 | "tooltip": { 433 | "shared": true, 434 | "value_type": "cumulative" 435 | }, 436 | "type": "graph", 437 | "x-axis": true, 438 | "y-axis": true, 439 | "y_formats": [ 440 | "bytes", 441 | "short" 442 | ] 443 | }, 444 | { 445 | "aliasColors": {}, 446 | "bars": false, 447 | "datasource": "Prometheus", 448 | "editable": true, 449 | "error": false, 450 | "fill": 1, 451 | "grid": { 452 | "leftLogBase": 1, 453 | "leftMax": null, 454 | "leftMin": null, 455 | "rightLogBase": 1, 456 | "rightMax": null, 457 | "rightMin": null, 458 | "threshold1": null, 459 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 460 | "threshold2": null, 461 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 462 | }, 463 | "height": "300", 464 | "id": 2, 465 | "legend": { 466 | "avg": true, 467 | "current": false, 468 | "hideEmpty": false, 469 | "max": false, 470 | "min": false, 471 | "rightSide": false, 472 | "show": true, 473 | "total": false, 474 | "values": true 475 | }, 476 | "lines": true, 477 | "linewidth": 2, 478 | "links": [], 479 | "nullPointMode": "null as zero", 480 | "percentage": false, 481 | "pointradius": 5, 482 | "points": false, 483 | "renderer": "flot", 484 | "seriesOverrides": [], 485 | "span": 9, 486 | "stack": false, 487 | "steppedLine": false, 488 | "targets": [ 489 | { 490 | "calculatedInterval": "2s", 491 | "datasourceErrors": {}, 492 | "errors": {}, 493 | "expr": "topk(5, sum(container_memory_usage_bytes{id=~\"^/docker/\",name=~\"$container\",node=~\"$node\",marathon_app_id=~\"$marathon_app\",image=~\"$image\",customer=~\"$customer\"}) by (name, node, marathon_app_id))", 494 | "interval": "", 495 | "intervalFactor": 2, 496 | "legendFormat": "{{ (typeof marathon_app_id != \"undefined\" ? marathon_app_id : name) }} on {{ node }}", 497 | "metric": "", 498 | "prometheusLink": "/api/datasources/proxy/1/graph#%5B%7B%22expr%22%3A%22topk(5%2C%20sum(container_memory_usage_bytes%7Bid%3D~%5C%22%5E%2Fdocker%2F%5C%22%2Cname%3D~%5C%22%24container%5C%22%2Cnode%3D~%5C%22%24node%5C%22%2Cmarathon_app_id%3D~%5C%22%24marathon_app%5C%22%2Cimage%3D~%5C%22%24image%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D)%20by%20(name%2C%20node%2C%20marathon_app_id))%22%2C%22range_input%22%3A%22900s%22%2C%22end_input%22%3A%222015-8-2%2011%3A10%22%2C%22step_input%22%3A%22%22%2C%22stacked%22%3Afalse%2C%22tab%22%3A0%7D%5D" 499 | } 500 | ], 501 | "timeFrom": null, 502 | "timeShift": null, 503 | "title": "Container Memory", 504 | "tooltip": { 505 | "shared": true, 506 | "value_type": "cumulative" 507 | }, 508 | "type": "graph", 509 | "x-axis": true, 510 | "y-axis": true, 511 | "y_formats": [ 512 | "bytes", 513 | "short" 514 | ] 515 | } 516 | ], 517 | "title": "New row" 518 | }, 519 | { 520 | "collapse": false, 521 | "editable": true, 522 | "height": "250px", 523 | "panels": [ 524 | { 525 | "aliasColors": {}, 526 | "bars": false, 527 | "datasource": null, 528 | "editable": true, 529 | "error": false, 530 | "fill": 1, 531 | "grid": { 532 | "leftLogBase": 1, 533 | "leftMax": null, 534 | "leftMin": null, 535 | "rightLogBase": 1, 536 | "rightMax": null, 537 | "rightMin": null, 538 | "threshold1": null, 539 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 540 | "threshold2": null, 541 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 542 | }, 543 | "id": 5, 544 | "legend": { 545 | "avg": false, 546 | "current": false, 547 | "max": false, 548 | "min": false, 549 | "show": false, 550 | "total": false, 551 | "values": false 552 | }, 553 | "lines": true, 554 | "linewidth": 2, 555 | "links": [], 556 | "nullPointMode": "connected", 557 | "percentage": false, 558 | "pointradius": 5, 559 | "points": false, 560 | "renderer": "flot", 561 | "seriesOverrides": [], 562 | "span": 6, 563 | "stack": false, 564 | "steppedLine": false, 565 | "targets": [ 566 | { 567 | "calculatedInterval": "2s", 568 | "datasourceErrors": {}, 569 | "errors": {}, 570 | "expr": "topk(5, rate(container_network_transmit_bytes_total{id=~\"^/docker/\",name=~\"$container\",node=~\"$node\",marathon_app_id=~\"$marathon_app\",image=~\"$image\",customer=~\"$customer\"}[1m]))", 571 | "interval": "", 572 | "intervalFactor": 2, 573 | "legendFormat": "{{ (typeof marathon_app_id != \"undefined\" ? marathon_app_id : name) }} on {{ node }}", 574 | "metric": "", 575 | "prometheusLink": "/api/datasources/proxy/1/graph#%5B%7B%22expr%22%3A%22topk(5%2C%20rate(container_network_transmit_bytes_total%7Bid%3D~%5C%22%5E%2Fdocker%2F%5C%22%2Cname%3D~%5C%22%24container%5C%22%2Cnode%3D~%5C%22%24node%5C%22%2Cmarathon_app_id%3D~%5C%22%24marathon_app%5C%22%2Cimage%3D~%5C%22%24image%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D%5B1m%5D))%22%2C%22range_input%22%3A%22901s%22%2C%22end_input%22%3A%222015-8-2%2011%3A10%22%2C%22step_input%22%3A%22%22%2C%22stacked%22%3Afalse%2C%22tab%22%3A0%7D%5D" 576 | } 577 | ], 578 | "timeFrom": null, 579 | "timeShift": null, 580 | "title": "Outgoing Traffic", 581 | "tooltip": { 582 | "shared": true, 583 | "value_type": "cumulative" 584 | }, 585 | "type": "graph", 586 | "x-axis": true, 587 | "y-axis": true, 588 | "y_formats": [ 589 | "bps", 590 | "short" 591 | ] 592 | }, 593 | { 594 | "aliasColors": {}, 595 | "bars": false, 596 | "datasource": "Prometheus", 597 | "editable": true, 598 | "error": false, 599 | "fill": 1, 600 | "grid": { 601 | "leftLogBase": 1, 602 | "leftMax": null, 603 | "leftMin": null, 604 | "rightLogBase": 1, 605 | "rightMax": null, 606 | "rightMin": null, 607 | "threshold1": null, 608 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 609 | "threshold2": null, 610 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 611 | }, 612 | "id": 3, 613 | "legend": { 614 | "alignAsTable": false, 615 | "avg": true, 616 | "current": false, 617 | "hideEmpty": false, 618 | "max": false, 619 | "min": false, 620 | "rightSide": false, 621 | "show": false, 622 | "total": false, 623 | "values": true 624 | }, 625 | "lines": true, 626 | "linewidth": 2, 627 | "links": [], 628 | "nullPointMode": "connected", 629 | "percentage": false, 630 | "pointradius": 5, 631 | "points": false, 632 | "renderer": "flot", 633 | "seriesOverrides": [], 634 | "span": 6, 635 | "stack": false, 636 | "steppedLine": false, 637 | "targets": [ 638 | { 639 | "calculatedInterval": "2s", 640 | "datasourceErrors": {}, 641 | "errors": {}, 642 | "expr": "topk(5, rate(container_network_receive_bytes_total{id=~\"^/docker/\",node=~\"$node\",name=~\"$container\",node=~\"$node\",marathon_app=~\"$marathon_app\",image=~\"$image\",customer=~\"$customer\"}[1m]))", 643 | "hide": false, 644 | "interval": "", 645 | "intervalFactor": 2, 646 | "legendFormat": "{{ (typeof marathon_app_id != \"undefined\" ? marathon_app_id : name) }} on {{ node }}", 647 | "metric": "", 648 | "prometheusLink": "/api/datasources/proxy/1/graph#%5B%7B%22expr%22%3A%22topk(5%2C%20rate(container_network_receive_bytes_total%7Bid%3D~%5C%22%5E%2Fdocker%2F%5C%22%2Cnode%3D~%5C%22%24node%5C%22%2Cname%3D~%5C%22%24container%5C%22%2Cnode%3D~%5C%22%24node%5C%22%2Cmarathon_app%3D~%5C%22%24marathon_app%5C%22%2Cimage%3D~%5C%22%24image%5C%22%2Ccustomer%3D~%5C%22%24customer%5C%22%7D%5B1m%5D))%22%2C%22range_input%22%3A%22900s%22%2C%22end_input%22%3A%222015-8-2%2011%3A10%22%2C%22step_input%22%3A%22%22%2C%22stacked%22%3Afalse%2C%22tab%22%3A0%7D%5D", 649 | "target": "" 650 | } 651 | ], 652 | "timeFrom": null, 653 | "timeShift": null, 654 | "title": "Incomming Traffic", 655 | "tooltip": { 656 | "shared": true, 657 | "value_type": "cumulative" 658 | }, 659 | "type": "graph", 660 | "x-axis": true, 661 | "y-axis": true, 662 | "y_formats": [ 663 | "Bps", 664 | "short" 665 | ] 666 | } 667 | ], 668 | "title": "New row" 669 | }, 670 | { 671 | "collapse": false, 672 | "editable": true, 673 | "height": "250px", 674 | "panels": [], 675 | "title": "New row" 676 | } 677 | ], 678 | "nav": [ 679 | { 680 | "collapse": false, 681 | "enable": true, 682 | "notice": false, 683 | "now": true, 684 | "refresh_intervals": [ 685 | "5s", 686 | "10s", 687 | "30s", 688 | "1m", 689 | "5m", 690 | "15m", 691 | "30m", 692 | "1h", 693 | "2h", 694 | "1d" 695 | ], 696 | "status": "Stable", 697 | "time_options": [ 698 | "5m", 699 | "15m", 700 | "1h", 701 | "6h", 702 | "12h", 703 | "24h", 704 | "2d", 705 | "7d", 706 | "30d" 707 | ], 708 | "type": "timepicker" 709 | } 710 | ], 711 | "time": { 712 | "from": "now-15m", 713 | "to": "now" 714 | }, 715 | "templating": { 716 | "list": [ 717 | { 718 | "allFormat": "regex wildcard", 719 | "current": { 720 | "text": "All", 721 | "value": ".*" 722 | }, 723 | "datasource": "Prometheus", 724 | "includeAll": true, 725 | "name": "node", 726 | "options": [ 727 | { 728 | "text": "All", 729 | "value": ".*" 730 | }, 731 | { 732 | "text": "node05", 733 | "value": "node05" 734 | }, 735 | { 736 | "text": "node04", 737 | "value": "node04" 738 | }, 739 | { 740 | "text": "node02", 741 | "value": "node02" 742 | }, 743 | { 744 | "text": "node03", 745 | "value": "node03" 746 | } 747 | ], 748 | "query": "container_last_seen{id=\"/\"}", 749 | "refresh": true, 750 | "refresh_on_load": false, 751 | "regex": "node=\"(.*)\"", 752 | "type": "query" 753 | }, 754 | { 755 | "allFormat": "regex wildcard", 756 | "current": { 757 | "text": "All", 758 | "value": ".*" 759 | }, 760 | "datasource": "Prometheus", 761 | "includeAll": true, 762 | "name": "container", 763 | "options": [ 764 | { 765 | "text": "All", 766 | "value": ".*" 767 | }, 768 | { 769 | "text": "weavescope", 770 | "value": "weavescope" 771 | }, 772 | { 773 | "text": "/docker/1fba0673c2f0ed1c4b9d7c6513ca7c33d59c4836346540175a4c10db22fd9999", 774 | "value": "/docker/1fba0673c2f0ed1c4b9d7c6513ca7c33d59c4836346540175a4c10db22fd9999" 775 | }, 776 | { 777 | "text": "cadvisor", 778 | "value": "cadvisor" 779 | }, 780 | { 781 | "text": "/docker/2877a49f2d3a476a74a7a14a2e164265b08a0e6af087faa2ab2573f5bbabcb11", 782 | "value": "/docker/2877a49f2d3a476a74a7a14a2e164265b08a0e6af087faa2ab2573f5bbabcb11" 783 | }, 784 | { 785 | "text": "mesos-dcca3d3d-9734-47ac-9bae-e7d17933d32d", 786 | "value": "mesos-dcca3d3d-9734-47ac-9bae-e7d17933d32d" 787 | }, 788 | { 789 | "text": "registrator", 790 | "value": "registrator" 791 | }, 792 | { 793 | "text": "mesos-d71137c9-5f84-43dd-bd93-bca98879d02c", 794 | "value": "mesos-d71137c9-5f84-43dd-bd93-bca98879d02c" 795 | }, 796 | { 797 | "text": "docker-controller", 798 | "value": "docker-controller" 799 | }, 800 | { 801 | "text": "/docker/58313bc8c6c423c0be5e4f49835a846f53b7eb158d50b8c05c56aa159a438a04", 802 | "value": "/docker/58313bc8c6c423c0be5e4f49835a846f53b7eb158d50b8c05c56aa159a438a04" 803 | }, 804 | { 805 | "text": "weave", 806 | "value": "weave" 807 | }, 808 | { 809 | "text": "/docker/ca7036eb2eca2e63dde167bca5b03ab643e09df456c90dd1c2eb245630a9737a", 810 | "value": "/docker/ca7036eb2eca2e63dde167bca5b03ab643e09df456c90dd1c2eb245630a9737a" 811 | }, 812 | { 813 | "text": "mesos-b6868c65-e490-46d6-a031-3b50f02a31e8", 814 | "value": "mesos-b6868c65-e490-46d6-a031-3b50f02a31e8" 815 | }, 816 | { 817 | "text": "/docker/6cd1292d7d9bbb36a1fc668454715537106601f197cb3fd52373db2ea454ffd4", 818 | "value": "/docker/6cd1292d7d9bbb36a1fc668454715537106601f197cb3fd52373db2ea454ffd4" 819 | }, 820 | { 821 | "text": "mesos-9ca00c89-9719-4d32-a7e5-b1ede4af45d6", 822 | "value": "mesos-9ca00c89-9719-4d32-a7e5-b1ede4af45d6" 823 | }, 824 | { 825 | "text": "/docker/bec343ad4f8900b1f20ff9f4f3a4dfb06331f6a1b90e480de4a025f6b487313c", 826 | "value": "/docker/bec343ad4f8900b1f20ff9f4f3a4dfb06331f6a1b90e480de4a025f6b487313c" 827 | }, 828 | { 829 | "text": "mesos-9f4307b5-f593-441d-97c4-bbdd3529ec2a", 830 | "value": "mesos-9f4307b5-f593-441d-97c4-bbdd3529ec2a" 831 | }, 832 | { 833 | "text": "/docker/d1fb08120b5b7423aef9cd159eafc398917026a306d50dfa76cd3984991c4fb2", 834 | "value": "/docker/d1fb08120b5b7423aef9cd159eafc398917026a306d50dfa76cd3984991c4fb2" 835 | }, 836 | { 837 | "text": "mesos-ea7cda57-076c-40cc-80d5-0b7a4ee7213a", 838 | "value": "mesos-ea7cda57-076c-40cc-80d5-0b7a4ee7213a" 839 | }, 840 | { 841 | "text": "/docker/5a4e8781db6e30cee40bcc2b520f64dfc148e2f79f6a192e4d22d9043569b3a5", 842 | "value": "/docker/5a4e8781db6e30cee40bcc2b520f64dfc148e2f79f6a192e4d22d9043569b3a5" 843 | } 844 | ], 845 | "query": "container_last_seen{id=~\"/docker/\"}", 846 | "refresh": true, 847 | "refresh_on_load": false, 848 | "regex": "name=\"(.*?)\"", 849 | "type": "query" 850 | }, 851 | { 852 | "allFormat": "regex wildcard", 853 | "current": { 854 | "text": "All", 855 | "value": ".*" 856 | }, 857 | "datasource": null, 858 | "includeAll": true, 859 | "name": "marathon_app", 860 | "options": [ 861 | { 862 | "text": "All", 863 | "value": ".*" 864 | }, 865 | { 866 | "text": "/monitoring/grafana", 867 | "value": "/monitoring/grafana" 868 | }, 869 | { 870 | "text": "/customer/2/app1", 871 | "value": "/customer/2/app1" 872 | }, 873 | { 874 | "text": "/customer/1/app1", 875 | "value": "/customer/1/app1" 876 | }, 877 | { 878 | "text": "/customer/2/app2", 879 | "value": "/customer/2/app2" 880 | }, 881 | { 882 | "text": "/db/phpmyadmin", 883 | "value": "/db/phpmyadmin" 884 | }, 885 | { 886 | "text": "/customer/1/app2", 887 | "value": "/customer/1/app2" 888 | } 889 | ], 890 | "query": "container_last_seen{marathon_app_id!=\"\"}", 891 | "refresh": true, 892 | "refresh_on_load": false, 893 | "regex": "marathon_app_id=\"(.*?)\"", 894 | "type": "query" 895 | }, 896 | { 897 | "allFormat": "regex wildcard", 898 | "current": { 899 | "text": "All", 900 | "value": ".*" 901 | }, 902 | "datasource": null, 903 | "includeAll": true, 904 | "name": "image", 905 | "options": [ 906 | { 907 | "text": "All", 908 | "value": ".*" 909 | } 910 | ], 911 | "query": "container_running", 912 | "refresh": true, 913 | "refresh_on_load": false, 914 | "regex": "image=\"(.*?)\"", 915 | "type": "query" 916 | }, 917 | { 918 | "allFormat": "regex wildcard", 919 | "current": { 920 | "text": "All", 921 | "value": ".*" 922 | }, 923 | "datasource": null, 924 | "includeAll": true, 925 | "name": "customer", 926 | "options": [ 927 | { 928 | "text": "All", 929 | "value": ".*" 930 | }, 931 | { 932 | "text": "0", 933 | "value": "0" 934 | }, 935 | { 936 | "text": "1", 937 | "value": "1" 938 | } 939 | ], 940 | "query": "container_last_seen{id=\"/\"}", 941 | "refresh": true, 942 | "refresh_on_load": false, 943 | "regex": "customer=\"(.*?)\"", 944 | "type": "query" 945 | } 946 | ] 947 | }, 948 | "annotations": { 949 | "list": [] 950 | }, 951 | "refresh": "10s", 952 | "schemaVersion": 6, 953 | "version": 1 954 | } --------------------------------------------------------------------------------