diff --git a/.gitignore b/.gitignore
index 648a292..d2f1235 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,33 @@
+<<<<<<< HEAD
+<<<<<<< HEAD
+down/*
+!down/download.sh
+!down/offline_images
+
+# k8s binaries directory
+bin/*
+!bin/readme.md
+
+# ansible hosts
+hosts
+
+# k8s storage manifests
+manifests/storage/*
+!manifests/storage/test.yaml
+
+# k8s backup directory
+roles/cluster-backup/files/*
+!roles/cluster-backup/files/readme.md
+
+# role based variable settings, exclude roles/os-harden/vars/
+/roles/*/vars/*
+!/roles/os-harden/vars/
+
+# cluster backups
+.cluster/
+=======
+=======
+>>>>>>> 53dfdbb4e1f8b5c083287ab4fca256fab9cdc731
# built application files
*.apk
*.ap_
@@ -27,3 +57,7 @@ proguard/
*.ipr
*.iws
.idea/
+<<<<<<< HEAD
+>>>>>>> Initial commit
+=======
+>>>>>>> 53dfdbb4e1f8b5c083287ab4fca256fab9cdc731
diff --git a/01.prepare.yml b/01.prepare.yml
new file mode 100644
index 0000000..fd1bcef
--- /dev/null
+++ b/01.prepare.yml
@@ -0,0 +1,24 @@
+# [optional] to synchronize time of nodes with 'chrony'
+- hosts: all
+ roles:
+ - { role: chrony, when: "hostvars[groups.deploy[0]]['NTP_ENABLED'] == 'yes'" }
+
+# to create CA, kubeconfig, kube-proxy.kubeconfig etc. on 'deploy' node
+- hosts: deploy
+ roles:
+ - deploy
+
+# prepare tasks for all nodes
+- hosts:
+ - kube-master
+ - kube-node
+ - deploy
+ - etcd
+ - lb
+ roles:
+ - prepare
+
+# [optional] to install loadbalance service, only needed by multi-master cluster
+- hosts: lb
+ roles:
+ - lb
diff --git a/02.etcd.yml b/02.etcd.yml
new file mode 100644
index 0000000..34a5941
--- /dev/null
+++ b/02.etcd.yml
@@ -0,0 +1,4 @@
+# to install etcd cluster
+- hosts: etcd
+ roles:
+ - etcd
diff --git a/03.docker.yml b/03.docker.yml
new file mode 100644
index 0000000..02f3a7e
--- /dev/null
+++ b/03.docker.yml
@@ -0,0 +1,6 @@
+# to install docker service
+- hosts:
+ - kube-master
+ - kube-node
+ roles:
+ - docker
diff --git a/04.kube-master.yml b/04.kube-master.yml
new file mode 100644
index 0000000..232ecfe
--- /dev/null
+++ b/04.kube-master.yml
@@ -0,0 +1,16 @@
+# to set up 'kube-master' nodes
+- hosts: kube-master
+ roles:
+ - kube-master
+ - kube-node
+ tasks:
+ - name: Making master nodes SchedulingDisabled
+ shell: "{{ bin_dir }}/kubectl cordon {{ inventory_hostname }} "
+ delegate_to: "{{ groups.deploy[0] }}"
+ when: DEPLOY_MODE != "allinone"
+ ignore_errors: true
+
+ - name: Setting master role name
+ shell: "{{ bin_dir }}/kubectl label node {{ inventory_hostname }} kubernetes.io/role=master --overwrite"
+ ignore_errors: true
+ delegate_to: "{{ groups.deploy[0] }}"
diff --git a/05.kube-node.yml b/05.kube-node.yml
new file mode 100644
index 0000000..5c7ddd3
--- /dev/null
+++ b/05.kube-node.yml
@@ -0,0 +1,4 @@
+# to set up 'kube-node' nodes
+- hosts: kube-node
+ roles:
+ - kube-node
diff --git a/06.network.yml b/06.network.yml
new file mode 100644
index 0000000..f683c36
--- /dev/null
+++ b/06.network.yml
@@ -0,0 +1,9 @@
+# to install network plugin, only one can be choosen
+- hosts:
+ - kube-master
+ - kube-node
+ roles:
+ - { role: calico, when: "CLUSTER_NETWORK == 'calico'" }
+ - { role: cilium, when: "CLUSTER_NETWORK == 'cilium'" }
+ - { role: flannel, when: "CLUSTER_NETWORK == 'flannel'" }
+ - { role: kube-router, when: "CLUSTER_NETWORK == 'kube-router'" }
diff --git a/07.cluster-addon.yml b/07.cluster-addon.yml
new file mode 100644
index 0000000..efe756f
--- /dev/null
+++ b/07.cluster-addon.yml
@@ -0,0 +1,5 @@
+# to install clust-addons
+- hosts:
+ - kube-node
+ roles:
+ - cluster-addon
diff --git a/1.txt b/1.txt
new file mode 100644
index 0000000..7c4a013
--- /dev/null
+++ b/1.txt
@@ -0,0 +1 @@
+aaa
\ No newline at end of file
diff --git a/11.harbor.yml b/11.harbor.yml
new file mode 100644
index 0000000..289580c
--- /dev/null
+++ b/11.harbor.yml
@@ -0,0 +1,41 @@
+# [optional] to set up a HARBOR, and to integrate the HARBOR with k8s cluster
+# read the guide: 'guide/harbor.md'
+
+- hosts: harbor
+ roles:
+ - { role: chrony, when: "hostvars[groups.deploy[0]]['NTP_ENABLED'] == 'yes' and NEW_INSTALL == 'yes'" }
+ - { role: prepare, when: "NEW_INSTALL == 'yes'" }
+ - { role: docker, when: "NEW_INSTALL == 'yes'" }
+ - { role: harbor, when: "NEW_INSTALL == 'yes'" }
+ tasks:
+ - name: Fetching the HARBOR SERVER's CA cert
+ fetch:
+ src: "{{ ca_dir }}/ca.pem"
+ dest: "{{ base_dir }}/down/"
+ flat: yes
+
+- hosts:
+ - kube-master
+ - kube-node
+ tasks:
+ - name: Define 'harbor_host', a domain
+ set_fact: harbor_host="{{ hostvars[groups.harbor[0]]['HARBOR_DOMAIN'] }}"
+
+ - name: Define 'harbor_host', an IP Addr
+ set_fact: harbor_host="{{ groups['harbor'][0] }}"
+ when: "hostvars[groups.harbor[0]]['HARBOR_DOMAIN'] == ''"
+
+ - name: Creating cert dir of the HARBOR SERVER for the docker daemon
+ file: name=/etc/docker/certs.d/{{ harbor_host }} state=directory
+
+ - name: Installing the HARBOR SERVER's cert on k8s nodes
+ copy: src={{ base_dir }}/down/ca.pem dest=/etc/docker/certs.d/{{ harbor_host }}/ca.crt
+
+ # [optional] if you have a DNS server, add an 'A record' instead
+ - name: Adding an '/etc/hosts' entry for the HARBOR DOMAIN
+ lineinfile:
+ dest: /etc/hosts
+ state: present
+ regexp: '{{ harbor_host }}'
+ line: "{{ groups['harbor'][0] }} {{ harbor_host }}"
+ when: "hostvars[groups.harbor[0]]['HARBOR_DOMAIN'] != ''"
diff --git a/22.upgrade.yml b/22.upgrade.yml
new file mode 100644
index 0000000..6a92d3a
--- /dev/null
+++ b/22.upgrade.yml
@@ -0,0 +1,23 @@
+# WARNING: Upgrade the k8s cluster can be risky. Make sure you know what you are doing.
+# Read the guide: 'op/upgrade.md' .
+
+# update kubectl binary
+- hosts:
+ - kube-master
+ - kube-node
+ - deploy
+ roles:
+ - prepare
+
+# update masters
+- hosts:
+ - kube-master
+ roles:
+ - kube-master
+ - kube-node
+
+# update nodes
+- hosts:
+ - kube-node
+ roles:
+ - { role: kube-node, when: "DEPLOY_MODE != 'allinone'" }
diff --git a/23.backup.yml b/23.backup.yml
new file mode 100644
index 0000000..3abad10
--- /dev/null
+++ b/23.backup.yml
@@ -0,0 +1,51 @@
+# cluster-backup playbook
+# read the guide: 'op/cluster_restore.md'
+
+- hosts:
+ - etcd
+ roles:
+ - cluster-backup
+
+- hosts:
+ - deploy
+ tasks:
+ - name: Creating backup dirs
+ file: name={{ item }} state=directory
+ with_items:
+ - "{{ base_dir }}/roles/cluster-backup/files/ca"
+ - "{{ base_dir }}/roles/cluster-backup/files/hosts"
+ - "{{ base_dir }}/roles/cluster-backup/files/snapshot"
+
+ - name: Backing up CA sth
+ copy:
+ src: "{{ ca_dir }}/{{ item }}"
+ dest: "{{ base_dir }}/roles/cluster-backup/files/ca/{{ item }}"
+ with_items:
+ - ca.pem
+ - ca-key.pem
+ - ca.csr
+ - ca-csr.json
+ - ca-config.json
+
+ - name: Backing up ansible hosts-1
+ copy:
+ src: "{{ base_dir }}/hosts"
+ dest: "{{ base_dir }}/roles/cluster-backup/files/hosts/hosts"
+ register: p
+
+ - name: Backing up ansible hosts-2
+ shell: "cd {{ base_dir }}/roles/cluster-backup/files/hosts && \
+ cp -fp hosts hosts-$(date +'%Y%m%d%H%M')"
+ when: 'p is changed'
+
+ - name: Backing up etcd snapshot-1
+ copy:
+ src: "{{ base_dir }}/roles/cluster-backup/files/snapshot.db"
+ dest: "{{ base_dir }}/roles/cluster-backup/files/snapshot/snapshot.db"
+ register: q
+
+ - name: Backing up etcd snapshot-2
+ shell: "cd {{ base_dir }}/roles/cluster-backup/files/ && \
+ mv -f snapshot.db snapshot/snapshot-$(date +'%Y%m%d%H%M').db"
+ when: 'q is changed'
+
diff --git a/24.restore.yml b/24.restore.yml
new file mode 100644
index 0000000..4b0467b
--- /dev/null
+++ b/24.restore.yml
@@ -0,0 +1,74 @@
+# cluster-restore playbook
+# read the guide: 'op/cluster_restore.md'
+
+# to restore CA sth on 'deploy' node
+- hosts: deploy
+ tasks:
+ - name: Restoring dirs of CA sth
+ file: name=/etc/kubernetes/ssl/ state=directory
+
+ - name: Restoring CA sth
+ copy:
+ src: "{{ base_dir }}/roles/cluster-backup/files/ca/{{ item }}"
+ dest: "{{ ca_dir }}/{{ item }}"
+ with_items:
+ - ca.pem
+ - ca-key.pem
+ - ca.csr
+ - ca-csr.json
+ - ca-config.json
+
+- hosts: deploy
+ roles:
+ - deploy
+
+# pre-tasks on all nodes
+- hosts: all
+ roles:
+ - prepare
+
+# [optional] only needed by multi-master cluster
+- hosts: lb
+ roles:
+ - lb
+
+# to install etcd cluster
+- hosts: etcd
+ roles:
+ - etcd
+
+# to install docker
+- hosts:
+ - kube-master
+ - kube-node
+ roles:
+ - docker
+
+# to set up 'kube-master' nodes
+- hosts:
+ - kube-master
+ roles:
+ - kube-master
+ - kube-node
+ #
+ tasks:
+ - name: Making master nodes SchedulingDisabled
+ shell: "{{ bin_dir }}/kubectl cordon {{ inventory_hostname }} "
+ when: DEPLOY_MODE != "allinone"
+ ignore_errors: true
+
+ - name: Setting master role name
+ shell: "{{ bin_dir }}/kubectl label node {{ inventory_hostname }} kubernetes.io/role=master --overwrite"
+ ignore_errors: true
+
+# to set up 'kube-node' nodes
+- hosts:
+ - kube-node
+ roles:
+ - kube-node
+
+# to restore data of etcd cluster
+- hosts: etcd
+ roles:
+ - cluster-restore
+
diff --git a/90.setup.yml b/90.setup.yml
new file mode 100644
index 0000000..c9e28cb
--- /dev/null
+++ b/90.setup.yml
@@ -0,0 +1,76 @@
+# [optional] to synchronize time of nodes with 'chrony'
+- hosts: all
+ roles:
+ - { role: chrony, when: "hostvars[groups.deploy[0]]['NTP_ENABLED'] == 'yes'" }
+
+# to create CA, kubeconfig, kube-proxy.kubeconfig etc. on 'deploy' node
+- hosts: deploy
+ roles:
+ - deploy
+
+# prepare tasks for all nodes
+- hosts:
+ - kube-master
+ - kube-node
+ - deploy
+ - etcd
+ - lb
+ roles:
+ - prepare
+
+# [optional] to install loadbalance service, only needed by multi-master cluster
+- hosts: lb
+ roles:
+ - lb
+
+# to install etcd cluster
+- hosts: etcd
+ roles:
+ - etcd
+
+# to install docker service
+- hosts:
+ - kube-master
+ - kube-node
+ roles:
+ - docker
+
+# to set up 'kube-master' nodes
+- hosts: kube-master
+ roles:
+ - kube-master
+ - kube-node
+ #
+ tasks:
+ - name: Making master nodes SchedulingDisabled
+ shell: "{{ bin_dir }}/kubectl cordon {{ inventory_hostname }} "
+ delegate_to: "{{ groups.deploy[0] }}"
+ when: DEPLOY_MODE != "allinone"
+ ignore_errors: true
+
+ - name: Setting master role name
+ shell: "{{ bin_dir }}/kubectl label node {{ inventory_hostname }} kubernetes.io/role=master --overwrite"
+ ignore_errors: true
+ delegate_to: "{{ groups.deploy[0] }}"
+
+# to set up 'kube-node' nodes
+- hosts: kube-node
+ roles:
+ - { role: kube-node, when: "DEPLOY_MODE != 'allinone'" }
+
+# to install network plugin, only one can be choosen
+- hosts:
+ - kube-master
+ - kube-node
+ roles:
+ - { role: calico, when: "CLUSTER_NETWORK == 'calico'" }
+ - { role: cilium, when: "CLUSTER_NETWORK == 'cilium'" }
+ - { role: flannel, when: "CLUSTER_NETWORK == 'flannel'" }
+ - { role: kube-router, when: "CLUSTER_NETWORK == 'kube-router'" }
+
+# to install clust-addons
+- hosts:
+ - kube-node
+ roles:
+ - cluster-addon
+
diff --git a/99.clean.yml b/99.clean.yml
new file mode 100644
index 0000000..ae10ffc
--- /dev/null
+++ b/99.clean.yml
@@ -0,0 +1,214 @@
+# WARNING: This playbook will erase the entire k8s-cluster, include PODs, ETCD data etc.
+# Make sure you know what you are doing.
+
+# to clean 'kube-node' nodes
+- hosts:
+ - kube-master
+ - kube-node
+ tasks:
+ - name: stop and disable kube-node service
+ service: name={{ item }} state=stopped enabled=no
+ with_items:
+ - kubelet
+ - kube-proxy
+ ignore_errors: true
+
+ - name: umount kubelet filesystems
+ shell: "mount | grep '/var/lib/kubelet'| awk '{print $3}'|xargs umount"
+ args:
+ warn: false
+ ignore_errors: true
+
+ - name: remove files and dirs of 'kube-node' nodes
+ file: name={{ item }} state=absent
+ with_items:
+ - "/var/lib/kubelet/"
+ - "/var/lib/kube-proxy/"
+ - "/etc/systemd/system/kubelet.service"
+ - "/etc/systemd/system/kube-proxy.service"
+ - "/opt/kube/kube-system/"
+
+# to clean 'kube-master' nodes
+- hosts:
+ - kube-master
+ tasks:
+ - name: stop and disable kube-master service
+ service: name={{ item }} state=stopped enabled=no
+ with_items:
+ - kube-apiserver
+ - kube-controller-manager
+ - kube-scheduler
+ ignore_errors: true
+
+ - name: remove files and dirs of 'kube-master' nodes
+ file: name={{ item }} state=absent
+ with_items:
+ - "/var/run/kubernetes"
+ - "/etc/systemd/system/kube-apiserver.service"
+ - "/etc/systemd/system/kube-controller-manager.service"
+ - "/etc/systemd/system/kube-scheduler.service"
+
+# to clean docker service and networking
+- hosts:
+ - kube-master
+ - kube-node
+ tasks:
+ - name: clean 'kube-router' stuff
+ shell: "{{ bin_dir }}/docker run --privileged --net=host cloudnativelabs/kube-router --cleanup-config"
+ ignore_errors: true
+ when: "CLUSTER_NETWORK == 'kube-router'"
+
+ - name: 获取是否运行名为'kubeasz'的容器
+ shell: 'docker ps|grep kubeasz || echo "NOT FOUND"'
+ register: install_info
+
+ - name: stop and disable docker service
+ service:
+ name: docker
+ state: stopped
+ enabled: no
+ ignore_errors: true
+ when: "'kubeasz' not in install_info.stdout"
+
+ # as k8s-network-plugins use host-network, '/var/run/docker/netns/default' must be umounted
+ - name: unmount docker filesystem-1
+ mount: path=/var/run/docker/netns/default state=unmounted
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: unmount docker filesystem-2
+ mount: path=/var/lib/docker/overlay state=unmounted
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: remove files and dirs
+ file: name={{ item }} state=absent
+ with_items:
+ - "/var/lib/docker/"
+ - "/var/run/docker/"
+ - "/etc/systemd/system/docker.service"
+ - "/etc/systemd/system/docker.service.requires/"
+ - "/etc/systemd/system/docker.service.d/"
+ - "/etc/bash_completion.d/docker"
+ - "/usr/bin/docker"
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: remove files and dirs2
+ file: name={{ item }} state=absent
+ with_items:
+ - "/etc/cni/"
+ - "/run/flannel/"
+ - "/etc/calico/"
+ - "/var/run/calico/"
+ - "/var/lib/calico/"
+ - "/var/log/calico/"
+ - "/etc/cilium/"
+ - "/var/run/cilium/"
+ - "/sys/fs/bpf/tc/"
+ - "/var/lib/cni/"
+ - "/var/lib/kube-router/"
+ - "/opt/kube/kube-system/"
+
+ - name: cleanup iptables
+ shell: "iptables -F && iptables -X \
+ && iptables -F -t nat && iptables -X -t nat \
+ && iptables -F -t raw && iptables -X -t raw \
+ && iptables -F -t mangle && iptables -X -t mangle"
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: cleanup networks1
+ shell: "ip link del tunl0; \
+ ip link del flannel.1; \
+ ip link del cni0; \
+ ip link del mynet0; \
+ ip link del kube-bridge; \
+ ip link del dummy0; \
+ ip link del kube-ipvs0; \
+ ip link del cilium_net; \
+ ip link del cilium_vxlan"
+ ignore_errors: true
+
+ - name: cleanup networks2
+ shell: "ip link del docker0; \
+ systemctl restart networking; \
+ systemctl restart network"
+ ignore_errors: true
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: cleanup 'calico' routes
+ shell: "for rt in `ip route|grep bird|sed 's/blackhole//'|awk '{print $1}'`;do ip route del $rt;done;"
+ when: "CLUSTER_NETWORK == 'calico'"
+ ignore_errors: true
+
+# to clean 'etcd' nodes
+- hosts: etcd
+ tasks:
+ - name: stop and disable etcd service
+ service:
+ name: etcd
+ state: stopped
+ enabled: no
+ ignore_errors: true
+
+ - name: remove files and dirs
+ file: name={{ item }} state=absent
+ with_items:
+ - "/var/lib/etcd"
+ - "/etc/etcd/"
+ - "/backup/k8s"
+ - "/etc/systemd/system/etcd.service"
+
+# to clean 'lb' nodes
+- hosts:
+ - lb
+ - ex-lb
+ tasks:
+ - name: stop keepalived service
+ shell: systemctl disable keepalived && systemctl stop keepalived
+ ignore_errors: true
+
+ - name: stop haproxy service
+ shell: systemctl disable haproxy && systemctl stop haproxy
+ ignore_errors: true
+
+ - name: remove files and dirs
+ file: name={{ item }} state=absent
+ with_items:
+ - "/etc/haproxy"
+ - "/etc/keepalived"
+
+# to clean ntp, certs and keys, env path
+- hosts:
+ - kube-master
+ - kube-node
+ - deploy
+ - etcd
+ - lb
+ tasks:
+ - name: stop and disable chrony in Ubuntu
+ service: name=chrony state=stopped enabled=no
+ ignore_errors: true
+ tags: rm_ntp
+ when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"
+
+ - name: stop and disable chronyd in CentOS/RedHat
+ service: name=chronyd state=stopped enabled=no
+ ignore_errors: true
+ tags: rm_ntp
+ when: ansible_distribution == "CentOS" or ansible_distribution == "RedHat"
+
+ - name: clean certs and keys
+ file: name={{ item }} state=absent
+ with_items:
+ - "/etc/kubernetes/"
+ - "{{ ca_dir }}"
+ - "/root/.kube/config"
+ - "/etc/docker/"
+
+ - name: clean 'ENV PATH'
+ lineinfile:
+ dest: ~/.bashrc
+ state: absent
+ regexp: '{{ item }}'
+ with_items:
+ - 'kubeasz'
+ - 'helm'
+ - 'kubectl completion'
diff --git a/README.md b/README.md
index 7473b15..54e0c10 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,116 @@
+<<<<<<< HEAD
+<<<<<<< HEAD
+# kubeasz - Ansible Spirit Zone In Kubernetes
+
+`kubeasz`致力于提供快速部署高可用`k8s`集群的工具, 并且也努力成为`k8s`实践、使用的参考书;基于二进制方式部署和利用`ansible-playbook`实现自动化:即提供一键安装脚本, 也可以分步执行安装各个组件, 同时讲解每一步主要参数配置和注意事项。
+
+**集群特性:`TLS`双向认证、`RBAC`授权、多`Master`高可用、支持`Network Policy`、备份恢复**
+
+|组件|支持|
+|:-|:-|
+|OS|Ubuntu 16.04+, CentOS/RedHat 7|
+|k8s|v1.8, v1.9, v1.10, v1.11, v1.12, v1.13|
+|etcd|v3.1, v3.2, v3.3|
+|docker|17.03.x-ce, 18.06.x-ce, 18.09.x|
+|network|calico, cilium, flannel, kube-router|
+
+- 注:集群用到的所有二进制文件已打包好供下载 [https://pan.baidu.com/s/1c4RFaA](https://pan.baidu.com/s/1c4RFaA)
+
+## 快速指南
+
+单机快速体验k8s集群的测试、开发环境--[AllinOne部署](docs/setup/quickStart.md)
+
+## 安装指南
+
+
+
+- 公有云上部署请阅读 [使用kubeasz在公有云上创建k8s集群](docs/setup/kubeasz_on_public_cloud.md)
+- 容器方式部署请阅读 [使用kubeasz容器创建k8s集群](docs/setup/docker_kubeasz.md)
+
+## 使用指南
+
+
+
+## 沟通交流
+
+- 微信群:k8s&kubeasz实践, 搜索微信号`badtobone`, 请备注(城市-github用户名), 验证通过会加入群聊。
+- 推荐阅读:[feisky-Kubernetes指南](https://github.com/feiskyer/kubernetes-handbook/blob/master/SUMMARY.md) [rootsongjc-Kubernetes指南](https://github.com/rootsongjc/kubernetes-handbook) [opsnull-安装教程](https://github.com/opsnull/follow-me-install-kubernetes-cluster)
+
+## 贡献&致谢
+
+请阅读[项目分支说明](docs/mixes/branch.md), 欢迎提[Issues](https://github.com/gjmzj/kubeasz/issues)和[PRs](docs/mixes/HowToContribute.md)参与维护项目!感谢您的关注与支持!
+
+- [如何 PR](docs/mixes/HowToContribute.md)
+- [如何捐赠](docs/mixes/donate.md)
+
+Copyright 2017 gjmzj (jmgaozz@163.com) Apache License 2.0, 详情见 [LICENSE](docs/mixes/LICENSE) 文件。
+=======
+=======
+>>>>>>> 53dfdbb4e1f8b5c083287ab4fca256fab9cdc731
VAB
===
//ZHOU FENG
VoiceAddreBook
+<<<<<<< HEAD
+>>>>>>> Initial commit
+=======
+>>>>>>> 53dfdbb4e1f8b5c083287ab4fca256fab9cdc731
diff --git a/ansible.cfg b/ansible.cfg
new file mode 100644
index 0000000..af38cc3
--- /dev/null
+++ b/ansible.cfg
@@ -0,0 +1,264 @@
+# config file for ansible -- http://ansible.com/
+# ==============================================
+
+# nearly all parameters can be overridden in ansible-playbook
+# or with command line flags. ansible will read ANSIBLE_CONFIG,
+# ansible.cfg in the current working directory, .ansible.cfg in
+# the home directory or /etc/ansible/ansible.cfg, whichever it
+# finds first
+
+[defaults]
+
+# some basic default values...
+
+#inventory = /etc/ansible/hosts
+#library = /usr/share/my_modules/
+#remote_tmp = $HOME/.ansible/tmp
+#forks = 5
+#poll_interval = 15
+#sudo_user = root
+#ask_sudo_pass = True
+#ask_pass = True
+#transport = smart
+#remote_port = 22
+#module_lang = C
+
+# plays will gather facts by default, which contain information about
+# the remote system.
+#
+# smart - gather by default, but don't regather if already gathered
+# implicit - gather by default, turn off with gather_facts: False
+# explicit - do not gather by default, must say gather_facts: True
+gathering = smart
+
+# additional paths to search for roles in, colon separated
+roles_path = /etc/ansible/roles
+
+# uncomment this to disable SSH key host checking
+host_key_checking = False
+
+# change the default callback
+#stdout_callback = skippy
+# enable additional callbacks
+#callback_whitelist = timer, mail
+
+# change this for alternative sudo implementations
+#sudo_exe = sudo
+
+# What flags to pass to sudo
+# WARNING: leaving out the defaults might create unexpected behaviours
+#sudo_flags = -H -S -n
+
+# SSH timeout
+#timeout = 10
+
+# default user to use for playbooks if user is not specified
+# (/usr/bin/ansible will use current user as default)
+#remote_user = root
+
+# logging is off by default unless this path is defined
+# if so defined, consider logrotate
+#log_path = /var/log/ansible.log
+
+# default module name for /usr/bin/ansible
+#module_name = command
+
+# use this shell for commands executed under sudo
+# you may need to change this to bin/bash in rare instances
+# if sudo is constrained
+#executable = /bin/sh
+
+# if inventory variables overlap, does the higher precedence one win
+# or are hash values merged together? The default is 'replace' but
+# this can also be set to 'merge'.
+#hash_behaviour = replace
+
+# by default, variables from roles will be visible in the global variable
+# scope. To prevent this, the following option can be enabled, and only
+# tasks and handlers within the role will see the variables there
+#private_role_vars = yes
+
+# list any Jinja2 extensions to enable here:
+#jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n
+
+# if set, always use this private key file for authentication, same as
+# if passing --private-key to ansible or ansible-playbook
+#private_key_file = /path/to/file
+
+# format of string {{ ansible_managed }} available within Jinja2
+# templates indicates to users editing templates files will be replaced.
+# replacing {file}, {host} and {uid} and strftime codes with proper values.
+#ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
+# This short version is better used in templates as it won't flag the file as changed every run.
+#ansible_managed = Ansible managed: {file} on {host}
+
+# by default, ansible-playbook will display "Skipping [host]" if it determines a task
+# should not be run on a host. Set this to "False" if you don't want to see these "Skipping"
+# messages. NOTE: the task header will still be shown regardless of whether or not the
+# task is skipped.
+display_skipped_hosts = False
+
+# by default (as of 1.3), Ansible will raise errors when attempting to dereference
+# Jinja2 variables that are not set in templates or action lines. Uncomment this line
+# to revert the behavior to pre-1.3.
+#error_on_undefined_vars = False
+
+# by default (as of 1.6), Ansible may display warnings based on the configuration of the
+# system running ansible itself. This may include warnings about 3rd party packages or
+# other conditions that should be resolved if possible.
+# to disable these warnings, set the following value to False:
+#system_warnings = True
+
+# by default (as of 1.4), Ansible may display deprecation warnings for language
+# features that should no longer be used and will be removed in future versions.
+# to disable these warnings, set the following value to False:
+#deprecation_warnings = True
+
+# (as of 1.8), Ansible can optionally warn when usage of the shell and
+# command module appear to be simplified by using a default Ansible module
+# instead. These warnings can be silenced by adjusting the following
+# setting or adding warn=yes or warn=no to the end of the command line
+# parameter string. This will for example suggest using the git module
+# instead of shelling out to the git command.
+# command_warnings = False
+
+
+# set plugin path directories here, separate with colons
+#action_plugins = /usr/share/ansible/plugins/action
+#callback_plugins = /usr/share/ansible/plugins/callback
+#connection_plugins = /usr/share/ansible/plugins/connection
+#lookup_plugins = /usr/share/ansible/plugins/lookup
+#vars_plugins = /usr/share/ansible/plugins/vars
+#filter_plugins = /usr/share/ansible/plugins/filter
+#test_plugins = /usr/share/ansible/plugins/test
+
+# by default callbacks are not loaded for /bin/ansible, enable this if you
+# want, for example, a notification or logging callback to also apply to
+# /bin/ansible runs
+#bin_ansible_callbacks = False
+
+
+# don't like cows? that's unfortunate.
+# set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1
+#nocows = 1
+
+# set which cowsay stencil you'd like to use by default. When set to 'random',
+# a random stencil will be selected for each task. The selection will be filtered
+# against the `cow_whitelist` option below.
+#cow_selection = default
+#cow_selection = random
+
+# when using the 'random' option for cowsay, stencils will be restricted to this list.
+# it should be formatted as a comma-separated list with no spaces between names.
+# NOTE: line continuations here are for formatting purposes only, as the INI parser
+# in python does not support them.
+#cow_whitelist=bud-frogs,bunny,cheese,daemon,default,dragon,elephant-in-snake,elephant,eyes,\
+# hellokitty,kitty,luke-koala,meow,milk,moofasa,moose,ren,sheep,small,stegosaurus,\
+# stimpy,supermilker,three-eyes,turkey,turtle,tux,udder,vader-koala,vader,www
+
+# don't like colors either?
+# set to 1 if you don't want colors, or export ANSIBLE_NOCOLOR=1
+#nocolor = 1
+
+# if set to a persistent type (not 'memory', for example 'redis') fact values
+# from previous runs in Ansible will be stored. This may be useful when
+# wanting to use, for example, IP information from one group of servers
+# without having to talk to them in the same playbook run to get their
+# current IP information.
+#fact_caching = memory
+
+
+# retry files
+# When a playbook fails by default a .retry file will be created in ~/
+# You can disable this feature by setting retry_files_enabled to False
+# and you can change the location of the files by setting retry_files_save_path
+
+retry_files_enabled = False
+#retry_files_save_path = ~/.ansible-retry
+
+
+# prevents logging of task data, off by default
+#no_log = False
+
+# prevents logging of tasks, but only on the targets, data is still logged on the master/controller
+#no_target_syslog = False
+
+# controls the compression level of variables sent to
+# worker processes. At the default of 0, no compression
+# is used. This value must be an integer from 0 to 9.
+#var_compression_level = 9
+
+[privilege_escalation]
+#become=True
+#become_method=sudo
+#become_user=root
+#become_ask_pass=False
+
+[paramiko_connection]
+
+# uncomment this line to cause the paramiko connection plugin to not record new host
+# keys encountered. Increases performance on new host additions. Setting works independently of the
+# host key checking setting above.
+#record_host_keys=False
+
+# by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment this
+# line to disable this behaviour.
+#pty=False
+
+[ssh_connection]
+
+# ssh arguments to use
+# Leaving off ControlPersist will result in poor performance, so use
+# paramiko on older platforms rather than removing it
+ssh_args = -o ControlMaster=auto -o ControlPersist=3600s
+
+# The path to use for the ControlPath sockets. This defaults to
+# "%(directory)s/ansible-ssh-%%h-%%p-%%r", however on some systems with
+# very long hostnames or very long path names (caused by long user names or
+# deeply nested home directories) this can exceed the character limit on
+# file socket names (108 characters for most platforms). In that case, you
+# may wish to shorten the string below.
+#
+# Example:
+# control_path = %(directory)s/%%h-%%r
+control_path = /tmp/ansible-ssh-%%h-%%p-%%r
+
+# Enabling pipelining reduces the number of SSH operations required to
+# execute a module on the remote server. This can result in a significant
+# performance improvement when enabled, however when using "sudo:" you must
+# first disable 'requiretty' in /etc/sudoers
+#
+# By default, this option is disabled to preserve compatibility with
+# sudoers configurations that have requiretty (the default on many distros).
+#
+pipelining = True
+
+# if True, make ansible use scp if the connection type is ssh
+# (default is sftp)
+#scp_if_ssh = True
+
+# if False, sftp will not use batch mode to transfer files. This may cause some
+# types of file transfer failures impossible to catch however, and should
+# only be disabled if your sftp version has problems with batch mode
+sftp_batch_mode = True
+
+[accelerate]
+#accelerate_port = 5099
+#accelerate_timeout = 30
+#accelerate_connect_timeout = 5.0
+
+# The daemon timeout is measured in minutes. This time is measured
+# from the last activity to the accelerate daemon.
+#accelerate_daemon_timeout = 30
+
+# If set to yes, accelerate_multi_key will allow multiple
+# private keys to be uploaded to it, though each user must
+# have access to the system via SSH to add a new key. The default
+# is "no".
+#accelerate_multi_key = yes
+
+[selinux]
+# file systems that require special treatment when dealing with security context
+# the default behaviour that copies the existing context or uses the user default
+# needs to be changed to use the file system dependent context.
+#special_context_filesystems=nfs,vboxsf,fuse,ramfs
diff --git a/bin/readme.md b/bin/readme.md
new file mode 100755
index 0000000..0cd1fcc
--- /dev/null
+++ b/bin/readme.md
@@ -0,0 +1,3 @@
+# Binaries for installing k8s
+
+Binaries can be downloaded at https://pan.baidu.com/s/1c4RFaA, alternatively they can be downloaded from official github repos separately.
diff --git a/dockerfiles/ansible/Dockerfile b/dockerfiles/ansible/Dockerfile
new file mode 100644
index 0000000..fbf468e
--- /dev/null
+++ b/dockerfiles/ansible/Dockerfile
@@ -0,0 +1,29 @@
+# Dockerfile for building Ansible image for Alpine 3
+# Origin from https://github.com/William-Yeh/docker-ansible
+
+FROM alpine:3.8
+
+RUN \
+ echo "===> Adding Python runtime..." && \
+ apk --update add python py-pip openssl ca-certificates && \
+ apk --update add --virtual build-dependencies \
+ python-dev libffi-dev openssl-dev build-base && \
+ pip install --upgrade pip cffi && \
+ \
+ \
+ echo "===> Installing Ansible..." && \
+ pip install ansible==2.6.12 && \
+ \
+ \
+ echo "===> Installing handy tools..." && \
+ pip install --upgrade pycrypto && \
+ apk --update add bash openssh-client rsync && \
+ \
+ \
+ echo "===> Removing package list..." && \
+ apk del build-dependencies && \
+ rm -rf /var/cache/apk/* && \
+ rm -rf /root/.cache
+
+# default command: display Ansible version
+CMD [ "ansible", "--version" ]
diff --git a/dockerfiles/kubeasz/Dockerfile b/dockerfiles/kubeasz/Dockerfile
new file mode 100644
index 0000000..f3c62dc
--- /dev/null
+++ b/dockerfiles/kubeasz/Dockerfile
@@ -0,0 +1,12 @@
+# NOTE: Prepare following Requirements and Build the image "kubeasz:$TAG"
+# Requirement 1: git clone https://github.com/gjmzj/kubeasz.git
+# Requirement 2: download binaries at https://pan.baidu.com/s/1c4RFaA, and put into dir 'kubeasz/bin'
+# Build: docker build -t kubeasz:$TAG .
+
+FROM jmgao1983/ansible:v2.6
+
+COPY kubeasz/ /etc/ansible
+
+RUN ln -s /etc/ansible/tools/easzctl /usr/bin/easzctl
+
+CMD [ "sleep", "360000000" ]
diff --git a/docs/guide/chrony.md b/docs/guide/chrony.md
new file mode 100644
index 0000000..684cdc2
--- /dev/null
+++ b/docs/guide/chrony.md
@@ -0,0 +1,146 @@
+# chrony 时间同步
+
+在安装k8s集群前需确保各节点时间同步;`chrony` 是一个优秀的 `NTP` 实现,性能比ntp好,且配置管理方便;它既可作时间服务器服务端,也可作客户端。
+
+- `OpenStack` 社区也推荐使用 `chrony`实现各节点之间的时间同步
+
+## 安装配置介绍
+
+项目中选定一个节点(`deploy` )作为集群内部其他节点的时间同步源,而 deploy节点本身从公网源同步;当然如果整个集群都无法访问公网,那么请手动校准deploy 节点的时间后,仍旧可以作为内部集群的时间源服务器。
+
+- 配置 chrony server, 在`/etc/chrony.conf` 配置以下几项,其他项默认值即可
+
+``` bash
+# 1. 配置时间源,国内可以增加阿里的时间源 ntp1.aliyun.com
+server {{ ntp_server }} iburst
+
+# 2. 配置允许同步的客户端网段
+allow {{ local_network }}
+
+# 3. 配置离线也能作为源服务器
+local stratum 10
+```
+
+- 配置 chrony client
+
+``` bash
+# 1. 清除所有其他时间源,只配置一个本地 deploy节点作为源
+server {{ groups.deploy[0] }} iburst
+
+# 2. 其他所有项可以默认配置
+```
+
+## `kubeasz` 集成安装
+
+- 修改 ansible hosts 文件,在 `deploy` 节点配置 `NTP_ENABLED=yes` (默认: no)
+- [可选] 修改 roles/chrony/var/main.yml 中的变量定义,关于文件 roles/chrony/var/main.yml 的由来请看[这里](../setup/config_guide.md)
+
+对于新集群或者新节点,`chrony` 的安装配置已经集成到 `90.setup.yml` `01.prepare.yml` `20.addnode.yml` `21.addmaster.yml` 等脚本中;对于已运行中的集群请执行如下命令进行安装:
+
+`ansible-playbook /etc/ansible/roles/chrony/chrony.yml `
+
+## 验证配置
+
+- 在 chrony server (deploy 节点)检查时间源信息,默认配置为`ntp1.aliyun.com`的地址:
+
+```
+$ chronyc sources -v
+210 Number of sources = 5
+
+ .-- Source mode '^' = server, '=' = peer, '#' = local clock.
+ / .- Source state '*' = current synced, '+' = combined , '-' = not combined,
+| / '?' = unreachable, 'x' = time may be in error, '~' = time too variable.
+|| .- xxxx [ yyyy ] +/- zzzz
+|| Reachability register (octal) -. | xxxx = adjusted offset,
+|| Log2(Polling interval) --. | | yyyy = measured offset,
+|| \ | | zzzz = estimated error.
+|| | | \
+MS Name/IP address Stratum Poll Reach LastRx Last sample
+===============================================================================
+^* 120.25.115.20 2 9 377 55 +147us[ +250us] +/- 15ms
+^- 85.199.214.100 1 10 377 182 -25ms[ -24ms] +/- 128ms
+^- makaki.miuku.net 2 10 367 307 +61ms[ +61ms] +/- 127ms
+^- static-5-103-139-163.ip.f 1 9 167 572 +532us[ +336us] +/- 117ms
+^- 119.28.183.184 2 7 377 33 -130us[ -130us] +/- 47ms
+```
+
+- 在 chrony server (deploy 节点)检查时间源同步状态
+
+```
+chronyc sourcestats -v
+210 Number of sources = 5
+ .- Number of sample points in measurement set.
+ / .- Number of residual runs with same sign.
+ | / .- Length of measurement set (time).
+ | | / .- Est. clock freq error (ppm).
+ | | | / .- Est. error in freq.
+ | | | | / .- Est. offset.
+ | | | | | | On the -.
+ | | | | | | samples. \
+ | | | | | | |
+Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
+==============================================================================
+120.25.115.20 15 11 44m +0.011 0.909 +4097ns 758us
+85.199.214.100 22 13 49m -3.588 5.097 -23ms 5709us
+makaki.miuku.net 22 14 46m +2.455 6.225 +64ms 4945us
+static-5-103-139-163.ip.f 20 13 42m -2.472 10.168 +3615us 6732us
+119.28.183.184 16 9 19m +10.378 25.190 +3469us 6803us
+```
+
+- 在 chrony client 检查,可以看到时间源只有一个(deploy 节点地址)
+
+```
+$ chronyc sources
+210 Number of sources = 1
+MS Name/IP address Stratum Poll Reach LastRx Last sample
+===============================================================================
+^* 192.168.1.1 3 6 377 15 +4085ns[ -25us] +/- 15ms
+$ chronyc sourcestats
+210 Number of sources = 1
+Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
+==============================================================================
+192.168.1.1 5 4 323 -0.252 0.819 -3031ns 15us
+```
+
+## 验证时间同步状态完成
+
+chrony 服务启动后,chrony server 会与配置的公网参考时间服务器进行同步;server 同步完成后,chrony client 会与 server 进行时间同步;一般来说整个集群达到时间同步需要几十分钟。可以用如下命令检查,初始时 **NTP synchronized: no**,同步完成后 **NTP synchronized: yes**
+
+``` bash
+$ ansible all -m shell -a 'timedatectl'
+192.168.1.1 | SUCCESS | rc=0 >>
+ Local time: Sat 2019-01-26 11:51:51 HKT
+ Universal time: Sat 2019-01-26 03:51:51 UTC
+ RTC time: Sat 2019-01-26 03:51:52
+ Time zone: Asia/Hong_Kong (HKT, +0800)
+ Network time on: yes
+NTP synchronized: yes
+ RTC in local TZ: no
+
+192.168.1.4 | SUCCESS | rc=0 >>
+ Local time: Sat 2019-01-26 11:51:51 HKT
+ Universal time: Sat 2019-01-26 03:51:51 UTC
+ RTC time: Sat 2019-01-26 03:51:52
+ Time zone: Asia/Hong_Kong (HKT, +0800)
+ Network time on: yes
+NTP synchronized: yes
+ RTC in local TZ: no
+
+192.168.1.2 | SUCCESS | rc=0 >>
+ Local time: Sat 2019-01-26 11:51:51 HKT
+ Universal time: Sat 2019-01-26 03:51:51 UTC
+ RTC time: Sat 2019-01-26 03:51:52
+ Time zone: Asia/Hong_Kong (HKT, +0800)
+ Network time on: yes
+NTP synchronized: yes
+ RTC in local TZ: no
+
+192.168.1.3 | SUCCESS | rc=0 >>
+ Local time: Sat 2019-01-26 11:51:51 HKT
+ Universal time: Sat 2019-01-26 03:51:51 UTC
+ RTC time: Sat 2019-01-26 03:51:52
+ Time zone: Asia/Hong_Kong (HKT, +0800)
+ Network time on: yes
+NTP synchronized: yes
+ RTC in local TZ: no
+```
diff --git a/docs/guide/dashboard.1.6.3.md b/docs/guide/dashboard.1.6.3.md
new file mode 100644
index 0000000..9a29479
--- /dev/null
+++ b/docs/guide/dashboard.1.6.3.md
@@ -0,0 +1,129 @@
+## dashboard
+
+本文档基于 dashboard 1.6.3版本,从 1.7.x 版本以后,dashboard 默认开启自带的登陆验证界面,登陆流程差异详见[新版本](dashboard.md)。
+
++ 注意:实际测试k8s版本<=1.9.1支持dashboard 1.6.3, 建议k8s 1.9 以后使用 dashboard 新版本。
+
+### 部署
+
+``` bash
+# 部署dashboard 主yaml配置文件
+$ kubectl create -f /etc/ansible/manifests/dashboard/1.6.3/kubernetes-dashboard.yaml
+# 部署基本密码认证配置[可选],密码文件位于 /etc/kubernetes/ssl/basic-auth.csv
+$ kubectl create -f /etc/ansible/manifests/dashboard/ui-admin-rbac.yaml
+$ kubectl create -f /etc/ansible/manifests/dashboard/ui-read-rbac.yaml
+```
+
+请在另外窗口打开 [kubernetes-dashboard.yaml](../../manifests/dashboard/1.6.3/kubernetes-dashboard.yaml)
+
++ 由于 kube-apiserver 启用了 RBAC授权,dashboard使用的 ServiceAccount `kubernetes-dashboard` 必须有相应的权限去访问apiserver(在新版本1.8.0中,该访问权限已按最小化方式授权),在1.6.3 版本,先粗放一点,把`kubernetes-dashboard` 与 集群角色 `cluster-admin` 绑定,这样dashboard就拥有了所有访问apiserver的权限。
++ 开发测试环境为了方便配置dashboard-service时候,指定 `NodePort`方式暴露服务,这样集群外部可以使用 `http://NodeIP:NodePort` 方式直接访问 dashboard,生产环境建议关闭该访问途径。
+
+### 验证
+
+``` bash
+# 查看pod 运行状态
+kubectl get pod -n kube-system | grep dashboard
+kubernetes-dashboard-86bd8778bf-w4974 1/1 Running 0 12h
+# 查看dashboard service
+kubectl get svc -n kube-system|grep dashboard
+kubernetes-dashboard NodePort 10.68.7.67 80:5452/TCP 12h
+# 查看集群服务
+kubectl cluster-info|grep dashboard
+kubernetes-dashboard is running at https://192.168.1.10:6443/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy
+# 查看pod 运行日志,关注有没有错误
+kubectl logs kubernetes-dashboard-86bd8778bf-w4974 -n kube-system
+```
+
+### 访问
+
+因为dashboard 作为k8s 原生UI,能够展示各种资源信息,甚至可以有修改、增加、删除权限,所以有必要对访问进行认证和控制,本项目预置部署的集群有以下安全设置:详见 [apiserver配置模板](../../roles/kube-master/templates/kube-apiserver.service.j2)
+
++ 启用 `TLS认证` `RBAC授权`等安全特性
++ 关闭 apiserver非安全端口8080的外部访问`--insecure-bind-address=127.0.0.1`
++ 关闭匿名认证`--anonymous-auth=false`
++ 补充启用基本密码认证 `--basic-auth-file=/etc/kubernetes/ssl/basic-auth.csv`,[密码文件模板](../../roles/kube-master/templates/basic-auth.csv.j2)中按照每行(密码,用户名,序号)的格式,可以定义多个用户
+
+#### 1. 临时访问:使用 `http://NodeIP:NodePort` 方式直接访问 dashboard,生产环境建议关闭该途径
+
+#### 2. 用户+密码访问:安全性比证书方式差点,务必保管好密码文件`basic-auth.csv`
+
+- 这里演示两种权限,使用admin 登陆dashboard拥有所有权限,使用readonly 登陆后仅查看权限,首先在 master节点文件 `/etc/kubernetes/ssl/basic-auth.csv` 确认用户名和密码,如果要增加或者修改用户,修改保存该文件后记得逐个重启你的master 节点
+- 为了演示用户密码访问,如果你已经完成证书访问方式,你可以在浏览器删除证书,或者访问时候浏览器询问你证书时不选证书
+- 2.1 设置用户admin 的RBAC 权限,如下运行配置文件 `kubectl create -f ui-admin-rbac.yaml`
+
+``` bash
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: ui-admin
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - services
+ - services/proxy
+ verbs:
+ - '*'
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: ui-admin-binding
+ namespace: kube-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: ui-admin
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+ kind: User
+ name: admin
+```
+- 2.2 设置用户readonly 的RBAC 权限,如下运行配置文件 `kubectl create -f ui-read-rbac.yaml`
+
+``` bash
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: ui-read
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - services
+ - services/proxy
+ verbs:
+ - get
+ - list
+ - watch
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: ui-read-binding
+ namespace: kube-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: ui-read
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+ kind: User
+ name: readonly
+```
+- 2.3 访问 `https://x.x.x.x:6443/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy` 使用 admin登陆拥有所有权限,比如删除某个部署;使用 readonly登陆只有查看权限,尝试删除某个部署会提示错误 `forbidden: User \"readonly\" cannot delete services/proxy in the namespace \"kube-system\"`
+
+#### 3. 证书访问:最安全的方式,配置较复杂
+- 使用集群CA 生成客户端证书,可以根据需要生成权限不同的证书,这里为了演示直接使用 kubectl使用的证书和key(在03.kubectl.yml阶段生成),该证书拥有所有权限
+- 指定格式导出该证书,进入`/etc/kubernetes/ssl`目录,使用命令`openssl pkcs12 -export -in admin.pem -inkey admin-key.pem -out kube-admin.p12` 提示输入证书密码和确认密码,可以用密码再增加一层保护,也可以直接回车跳过,完成后目录下多了 `kube-admin.p12`文件,将它分发给授权的用户
+- 用户将 `kube-admin.p12` 双击导入证书即可,`IE` 和`Chrome` 中输入`https://x.x.x.x:6443/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy` 或者 `https://x.x.x.x:6443/ui` 即可访问。补充:最新firefox需要在浏览器中单独导入 [选项] - [隐私与安全] - [证书/查看证书] - [您的证书] 页面点击 [导入] 该证书
+
+### 小结
+
++ dashboard 版本 1.6.3 访问控制实现较复杂,文档中给出的例子也有助于你理解 RBAC的灵活控制能力,当然最好去[官方文档](https://kubernetes.io/docs/admin/authorization/rbac/)学习一下,这块篇幅不长
++ 由于还未部署 Heapster 插件,当前 dashboard 不能展示 Pod、Nodes 的 CPU、内存等 metric 图形,后续部署 heapster后自然能够看到
++ 本文中的权限设置仅供演示用,生产环境请在此基础上修改成适合你安全需求的方式
+
diff --git a/docs/guide/dashboard.md b/docs/guide/dashboard.md
new file mode 100644
index 0000000..86cc65f
--- /dev/null
+++ b/docs/guide/dashboard.md
@@ -0,0 +1,121 @@
+## dashboard
+
+本文档基于 dashboard 1.10.0版本,k8s版本 1.11.x。因 dashboard 1.7 以后默认开启了自带的登陆验证机制,因此不同版本登陆有差异:
+
+- 旧版(<= 1.6)建议通过apiserver访问,直接通过apiserver 认证授权机制去控制 dashboard权限,详见[旧版文档](dashboard.1.6.3.md)
+- 新版(>= 1.7)可以使用自带的登陆界面,使用不同Service Account Tokens 去控制访问 dashboard的权限
+
+### 部署
+
+如果之前已按照本项目部署dashboard1.6.3,先删除旧版本:`kubectl delete -f /etc/ansible/manifests/dashboard/1.6.3/`
+
+新版配置文件参考[官方文档](https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml)
+
++ 增加了通过`api-server`方式访问dashboard
++ 增加了`NodePort`方式暴露服务,这样集群外部可以使用 `https://NodeIP:NodePort` (注意是https不是http,区别于1.6.3版本) 直接访问 dashboard。
+
+安装部署
+
+``` bash
+# 部署dashboard 主yaml配置文件
+$ kubectl apply -f /etc/ansible/manifests/dashboard/kubernetes-dashboard.yaml
+# [可选]部署基本密码认证配置,使用apiserver 方式访问需要
+$ kubectl apply -f /etc/ansible/manifests/dashboard/ui-admin-rbac.yaml
+$ kubectl apply -f /etc/ansible/manifests/dashboard/ui-read-rbac.yaml
+# 创建可读可写 admin Service Account
+$ kubectl apply -f /etc/ansible/manifests/dashboard/admin-user-sa-rbac.yaml
+# 创建只读 read Service Account
+$ kubectl apply -f /etc/ansible/manifests/dashboard/read-user-sa-rbac.yaml
+```
+
+### 验证
+
+``` bash
+# 查看pod 运行状态
+kubectl get pod -n kube-system | grep dashboard
+kubernetes-dashboard-7c74685c48-9qdpn 1/1 Running 0 22s
+# 查看dashboard service
+kubectl get svc -n kube-system|grep dashboard
+kubernetes-dashboard NodePort 10.68.219.38 443:24108/TCP 53s
+# 查看集群服务
+kubectl cluster-info|grep dashboard
+kubernetes-dashboard is running at https://192.168.1.1:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy
+# 查看pod 运行日志
+kubectl logs kubernetes-dashboard-7c74685c48-9qdpn -n kube-system
+```
+
++ 由于还未部署 Heapster 插件,当前 dashboard 不能展示 Pod、Nodes 的 CPU、内存等 metric 图形,后续部署 heapster后自然能够看到
+
+### 访问控制
+
+因为dashboard 作为k8s 原生UI,能够展示各种资源信息,甚至可以有修改、增加、删除权限,所以有必要对访问进行认证和控制,本项目部署的集群有以下安全设置:详见 [apiserver配置模板](../../roles/kube-master/templates/kube-apiserver.service.j2)
+
++ 启用 `TLS认证` `RBAC授权`等安全特性
++ 关闭 apiserver非安全端口8080的外部访问`--insecure-bind-address=127.0.0.1`
++ 关闭匿名认证`--anonymous-auth=false`
++ 补充启用基本密码认证 `--basic-auth-file=/etc/kubernetes/ssl/basic-auth.csv`,[密码文件模板](../../roles/kube-master/templates/basic-auth.csv.j2)中按照每行(密码,用户名,序号)的格式,可以定义多个用户
+
+新版 dashboard可以有多层访问控制,首先与旧版一样可以使用apiserver 方式登陆控制:
+
++ 第一步通过api-server本身安全认证流程,与之前[1.6.3版本](dashboard.1.6.3.md)相同,这里不再赘述
++ 第二步通过dashboard自带的登陆流程,使用`Kubeconfig` `Token`等方式登陆
+
+**注意:** 如果集群已启用 ingress tls的话,可以[配置ingress规则访问dashboard](ingress-tls.md#%E9%85%8D%E7%BD%AE-dashboard-ingress)
+
+### 演示新登陆方式
+
+为演示方便这里使用 `https://NodeIP:NodePort` 方式访问 dashboard,支持两种登录方式:Kubeconfig、令牌(Token)
+
+- 令牌登录(admin)
+
+选择“令牌(Token)”方式登陆,复制下面输出的admin token 字段到输入框
+
+``` bash
+# 创建Service Account 和 ClusterRoleBinding
+$ kubectl apply -f /etc/ansible/manifests/dashboard/admin-user-sa-rbac.yaml
+# 获取 Bearer Token,找到输出中 ‘token:’ 开头那一行
+$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')
+```
+
+- 令牌登录(只读)
+
+选择“令牌(Token)”方式登陆,复制下面输出的read token 字段到输入框
+
+``` bash
+# 创建Service Account 和 ClusterRoleBinding
+$ kubectl apply -f /etc/ansible/manifests/dashboard/read-user-sa-rbac.yaml
+# 获取 Bearer Token,找到输出中 ‘token:’ 开头那一行
+$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep read-user | awk '{print $1}')
+```
+- Kubeconfig登录(admin)
+Admin kubeconfig文件默认位置:`/root/.kube/config`,该文件中默认没有token字段,使用Kubeconfig方式登录,还需要将token追加到该文件中,完整的文件格式如下:
+```
+apiVersion: v1
+clusters:
+- cluster:
+ certificate-authority-data: LS0tLS1CRUdxxxxxxxxxxxxxx
+ server: https://192.168.1.2:6443
+ name: kubernetes
+contexts:
+- context:
+ cluster: kubernetes
+ user: admin
+ name: kubernetes
+current-context: kubernetes
+kind: Config
+preferences: {}
+users:
+- name: admin
+ user:
+ client-certificate-data: LS0tLS1CRUdJTiBDRxxxxxxxxxxx
+ client-key-data: LS0tLS1CRUdJTxxxxxxxxxxxxxx
+ token: eyJhbGcixxxxxxxxxxxxxxxx
+```
+
+- Kubeconfig登陆(只读)
+首先[创建只读权限 kubeconfig文件](../op/readonly_kubectl.md),然后类似追加只读token到该文件,略。
+
+### 参考
+
+- 1. [Dashboard Access control](https://github.com/kubernetes/dashboard/wiki/Access-control)
+- 2. [a-read-only-kubernetes-dashboard](https://blog.cowger.us/2018/07/03/a-read-only-kubernetes-dashboard.html)
diff --git a/docs/guide/efk.md b/docs/guide/efk.md
new file mode 100644
index 0000000..aaa4325
--- /dev/null
+++ b/docs/guide/efk.md
@@ -0,0 +1,195 @@
+### 第一部分:EFK
+
+`EFK` 插件是`k8s`项目的一个日志解决方案,它包括三个组件:[Elasticsearch](), [Fluentd](), [Kibana]();Elasticsearch 是日志存储和日志搜索引擎,Fluentd 负责把`k8s`集群的日志发送给 Elasticsearch, Kibana 则是可视化界面查看和检索存储在 Elasticsearch 的数据。
+
+### 准备
+
+下载官方最新[release](https://github.com/kubernetes/kubernetes/release),进入目录: `kubernetes/cluster/addons/fluentd-elasticsearch`,参考官方配置的基础上使用本项目`manifests/efk/`部署,以下为几点主要的修改:
+
++ 官方提供的`kibana-deployment.yaml`中的参数`SERVER_BASEPATH`在k8s v1.8 版本以后部署需要按照本项目调整
++ 修改官方docker镜像,方便国内下载加速
+
+### 安装
+
+``` bash
+$ kubectl create -f /etc/ansible/manifests/efk/
+$ kubectl create -f /etc/ansible/manifests/efk/es-without-pv/
+```
+
+**注意**:Fluentd 是以 DaemonSet 形式运行且只会调度到有`beta.kubernetes.io/fluentd-ds-ready=true`标签的节点,所以对需要收集日志的节点逐个打上标签:
+
+``` bash
+$ kubectl label nodes 192.168.1.2 beta.kubernetes.io/fluentd-ds-ready=true
+node "192.168.1.2" labeled
+```
+
+### 验证
+
+``` bash
+kubectl get pods -n kube-system|grep -E 'elasticsearch|fluentd|kibana'
+elasticsearch-logging-0 1/1 Running 0 19h
+elasticsearch-logging-1 1/1 Running 0 19h
+fluentd-es-v2.0.2-6c95c 1/1 Running 0 17h
+fluentd-es-v2.0.2-f2xh8 1/1 Running 0 8h
+fluentd-es-v2.0.2-pv5q5 1/1 Running 0 8h
+kibana-logging-d5cffd7c6-9lz2p 1/1 Running 0 1m
+```
+kibana Pod 第一次启动时会用较长时间(10-20分钟)来优化和 Cache 状态页面,可以查看 Pod 的日志观察进度,如下等待 `Ready` 状态
+
+``` bash
+$ kubectl logs -n kube-system kibana-logging-d5cffd7c6-9lz2p -f
+...
+{"type":"log","@timestamp":"2018-03-13T07:33:00Z","tags":["listening","info"],"pid":1,"message":"Server running at http://0:5601"}
+{"type":"log","@timestamp":"2018-03-13T07:33:00Z","tags":["status","ui settings","info"],"pid":1,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
+```
+
+### 访问 Kibana
+
+这里介绍 `kube-apiserver`方式访问,获取访问 URL
+
+``` bash
+$ kubectl cluster-info | grep Kibana
+Kibana is running at https://192.168.1.10:8443/api/v1/namespaces/kube-system/services/kibana-logging/proxy
+```
+浏览器访问 URL:`https://192.168.1.10:8443/api/v1/namespaces/kube-system/services/kibana-logging/proxy`,然后使用`basic auth(参照hosts文件设置,默认:用户admin 密码test1234)`或者`证书` 的方式认证后即可,关于认证可以参考[dashboard文档](dashboard.md)
+
+首次登陆需要在`Management` - `Index Patterns` 创建 `index pattern`,可以使用默认的 logstash-* pattern,点击 Create; 创建Index后,稍等几分钟就可以在 Discover 菜单看到 ElasticSearch logging 中汇聚的日志;
+
+### 第二部分:日志持久化之静态PV
+日志数据是存放于 `Elasticsearch POD`中,但是默认情况下它使用的是`emptyDir`存储类型,所以当 `POD`被删除或重新调度时,日志数据也就丢失了。以下讲解使用`NFS` 服务器手动(静态)创建`PV` 持久化保存日志数据的例子。
+
+#### 配置 NFS
+
++ 准备一个nfs服务器,如果没有可以参考[nfs-server](nfs-server.md)创建。
++ 配置nfs服务器的共享目录,即修改`/etc/exports`(根据实际网段替换`192.168.1.*`),修改后重启`systemctl restart nfs-server`。
+
+``` bash
+/share 192.168.1.*(rw,sync,insecure,no_subtree_check,no_root_squash)
+/share/es0 192.168.1.*(rw,sync,insecure,no_subtree_check,no_root_squash)
+/share/es1 192.168.1.*(rw,sync,insecure,no_subtree_check,no_root_squash)
+/share/es2 192.168.1.*(rw,sync,insecure,no_subtree_check,no_root_squash)
+```
+
+#### 使用静态 PV安装 EFK
+
+- 请按实际日志容量需求修改 `es-static-pv/es-statefulset.yaml` 文件中 volumeClaimTemplates 设置的 storage: 4Gi 大小
+- 请根据实际nfs服务器地址、共享目录、容量大小修改 `es-static-pv/es-pv*.yaml` 文件中对应的设置
+
+``` bash
+# 如果之前已经安装了默认的EFK,请用以下两个命令先删除它
+$ kubectl delete -f /etc/ansible/manifests/efk/
+$ kubectl delete -f /etc/ansible/manifests/efk/es-without-pv/
+
+# 安装静态PV 的 EFK
+$ kubectl create -f /etc/ansible/manifests/efk/
+$ kubectl create -f /etc/ansible/manifests/efk/es-static-pv/
+```
++ 目录`es-static-pv` 下首先是利用 NFS服务预定义了三个 PV资源,然后在 `es-statefulset.yaml`定义中使用 `volumeClaimTemplates` 去匹配使用预定义的 PV资源;注意 PV参数:`accessModes` `storageClassName` `storage`容量大小必须两边匹配。
+
+#### 验证安装
+
++ 1.集群中查看 `pod` `pv` `pvc` 等资源
+
+``` bash
+$ kubectl get pods -n kube-system|grep -E 'elasticsearch|fluentd|kibana'
+elasticsearch-logging-0 1/1 Running 0 10m
+elasticsearch-logging-1 1/1 Running 0 10m
+fluentd-es-v2.0.2-6c95c 1/1 Running 0 10m
+fluentd-es-v2.0.2-f2xh8 1/1 Running 0 10m
+fluentd-es-v2.0.2-pv5q5 1/1 Running 0 10m
+kibana-logging-d5cffd7c6-9lz2p 1/1 Running 0 10m
+
+$ kubectl get pv
+NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
+pv-es-0 4Gi RWX Recycle Bound kube-system/elasticsearch-logging-elasticsearch-logging-0 es-storage-class 1m
+pv-es-1 4Gi RWX Recycle Bound kube-system/elasticsearch-logging-elasticsearch-logging-1 es-storage-class 1m
+pv-es-2 4Gi RWX Recycle Available es-storage-class 1m
+
+$ kubectl get pvc --all-namespaces
+NAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
+kube-system elasticsearch-logging-elasticsearch-logging-0 Bound pv-es-0 4Gi RWX es-storage-class 2m
+kube-system elasticsearch-logging-elasticsearch-logging-1 Bound pv-es-1 4Gi RWX es-storage-class 1m
+```
+
++ 2.网页访问 `kibana`查看具体的日志,如上须等待(约15分钟) `kibana Pod`优化和 Cache 状态页面,达到 `Ready` 状态。
+
++ 3.登陆 NFS Server 查看对应目录和内部数据
+
+``` bash
+$ ls /share
+es0 es1 es2
+```
+
+### 第三部分:日志持久化之动态PV
+`PV` 作为集群的存储资源,`StatefulSet` 依靠它实现 POD的状态数据持久化,但是当 `StatefulSet`动态伸缩时,它的 `PVC`请求也会变化,如果每次都需要管理员手动去创建对应的 `PV`资源,那就很不方便;因此 K8S还提供了 `provisioner`来动态创建 `PV`,不仅节省了管理员的时间,还可以根据不同的 `StorageClasses`封装不同类型的存储供 PVC 选用。
+
++ 此功能需要 `API-SERVER` 参数 `--admission-control`字符串设置中包含 `DefaultStorageClass`,本项目中已经开启。
++ `provisioner`指定 Volume 插件的类型,包括内置插件(如 kubernetes.io/glusterfs)和外部插件(如 external-storage 提供的 ceph.com/cephfs,nfs-client等),以下讲解使用 `nfs-client-provisioner`来动态创建 `PV`来持久化保存 `EFK`的日志数据。
+
+#### 配置 NFS(同上)
+
+确保 `/etc/exports` 配置如下共享目录,并确保 `/share`目录可读可写权限,否则可能因为权限问题无法动态生成 PV的对应目录。(根据实际情况替换IP段`192.168.1.*`)
+``` bash
+/share 192.168.1.*(rw,sync,insecure,no_subtree_check,no_root_squash)
+```
+
+#### 使用动态 PV安装 EFK
+
+- 首先根据[集群存储](../setup/08-cluster-storage.md)创建nfs-client-provisioner
+- 然后按实际需求修改 `es-dynamic-pv/es-statefulset.yaml` 文件中 volumeClaimTemplates 设置的 storage: 4Gi 大小
+
+``` bash
+# 如果之前已经安装了默认的EFK或者静态PV EFK,请用以下命令先删除它
+$ kubectl delete -f /etc/ansible/manifests/efk/
+$ kubectl delete -f /etc/ansible/manifests/efk/es-without-pv/
+$ kubectl delete -f /etc/ansible/manifests/efk/es-static-pv/
+
+# 安装动态PV 的 EFK
+$ kubectl create -f /etc/ansible/manifests/efk/
+$ kubectl create -f /etc/ansible/manifests/efk/es-dynamic-pv/
+```
++ 首先 `nfs-client-provisioner.yaml` 创建一个工作 POD,它监听集群的 PVC请求,并当 PVC请求来到时调用 `nfs-client` 去请求 `nfs-server`的存储资源,成功后即动态生成对应的 PV资源。
++ `nfs-dynamic-storageclass.yaml` 定义 NFS存储类型的类型名 `nfs-dynamic-class`,然后在 `es-statefulset.yaml`中必须使用这个类型名才能动态请求到资源。
+
+#### 验证安装
+
++ 1.集群中查看 `pod` `pv` `pvc` 等资源
+
+``` bash
+$ kubectl get pods -n kube-system|grep -E 'elasticsearch|fluentd|kibana'
+elasticsearch-logging-0 1/1 Running 0 10m
+elasticsearch-logging-1 1/1 Running 0 10m
+fluentd-es-v2.0.2-6c95c 1/1 Running 0 10m
+fluentd-es-v2.0.2-f2xh8 1/1 Running 0 10m
+fluentd-es-v2.0.2-pv5q5 1/1 Running 0 10m
+kibana-logging-d5cffd7c6-9lz2p 1/1 Running 0 10m
+
+$ kubectl get pv
+NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
+pvc-50644f36-358b-11e8-9edd-525400cecc16 4Gi RWX Delete Bound kube-system/elasticsearch-logging-elasticsearch-logging-0 nfs-dynamic-class 10m
+pvc-5b105ee6-358b-11e8-9edd-525400cecc16 4Gi RWX Delete Bound kube-system/elasticsearch-logging-elasticsearch-logging-1 nfs-dynamic-class 10m
+
+$ kubectl get pvc --all-namespaces
+NAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
+kube-system elasticsearch-logging-elasticsearch-logging-0 Bound pvc-50644f36-358b-11e8-9edd-525400cecc16 4Gi RWX nfs-dynamic-class 10m
+kube-system elasticsearch-logging-elasticsearch-logging-1 Bound pvc-5b105ee6-358b-11e8-9edd-525400cecc16 4Gi RWX nfs-dynamic-class 10m
+```
+
++ 2.网页访问 `kibana`查看具体的日志,如上须等待(约15分钟) `kibana Pod`优化和 Cache 状态页面,达到 `Ready` 状态。
+
++ 3.登陆 NFS Server 查看对应目录和内部数据
+
+``` bash
+$ ls /share # 可以看到类似如下的目录生成
+kube-system-elasticsearch-logging-elasticsearch-logging-0-pvc-50644f36-358b-11e8-9edd-525400cecc16
+kube-system-elasticsearch-logging-elasticsearch-logging-1-pvc-5b105ee6-358b-11e8-9edd-525400cecc16
+```
+
+### 参考
+
+1. [EFK 配置](https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch)
+1. [nfs-client-provisioner](https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client)
+1. [persistent-volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims)
+1. [storage-classes](https://kubernetes.io/docs/concepts/storage/storage-classes/)
+
+
diff --git a/docs/guide/gitlab/app.yaml.md b/docs/guide/gitlab/app.yaml.md
new file mode 100644
index 0000000..f6f2225
--- /dev/null
+++ b/docs/guide/gitlab/app.yaml.md
@@ -0,0 +1,90 @@
+## 3.3 K8S 应用部署模板 app.yaml
+
+以下示例配置仅做参考,描述一个简单 java spring boot项目的 k8s 部署文件模板;在实际部署前,CI/CD流程中会对变量做替换。详见 [gitlab-ci.yml文件](gitlab-ci.yml.md)。
+
+``` bash
+cat > .ci/app.yaml << EOF
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: PROJECT_NS
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: harborkey1
+ namespace: PROJECT_NS
+data:
+ #待替换的变量DOCKER_KEY,参考 docs/guide/harbor.md#k8s%E4%B8%AD%E4%BD%BF%E7%94%A8harbor
+ .dockerconfigjson: DOCKER_KEY
+type: kubernetes.io/dockerconfigjson
+
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: APP_NAME
+ namespace: PROJECT_NS
+spec:
+ replicas: APP_REP
+ template:
+ metadata:
+ labels:
+ run: APP_NAME
+ spec:
+ containers:
+ - name: APP_NAME
+ image: ProjectImage
+ imagePullPolicy: Always
+ env:
+ # 设置java的时区
+ - name: TZ
+ value: "Asia/Shanghai"
+ resources:
+ limits:
+ cpu: 500m
+ memory: 1600Mi
+ requests:
+ cpu: 200m
+ memory: 800Mi
+ ports:
+ - containerPort: 8080
+ imagePullSecrets:
+ - name: harborkey1
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ run: APP_NAME
+ name: APP_NAME
+ namespace: PROJECT_NS
+spec:
+ ports:
+ - port: 80
+ protocol: TCP
+ targetPort: 8080
+ selector:
+ run: APP_NAME
+ sessionAffinity: None
+
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: APP_NAME-ingress
+ namespace: PROJECT_NS
+spec:
+ rules:
+ - host: AppDomain
+ http:
+ paths:
+ - path: /AppPath
+ backend:
+ serviceName: APP_NAME
+ servicePort: 80
+EOF
+```
+
diff --git a/docs/guide/gitlab/config.sh.md b/docs/guide/gitlab/config.sh.md
new file mode 100644
index 0000000..256dd30
--- /dev/null
+++ b/docs/guide/gitlab/config.sh.md
@@ -0,0 +1,49 @@
+## 3.2 环境配置替换 config.sh
+
+首先应用开发人员需要整理在不同环境(测试环境/生产环境)的配置参数,并在源代码中约定好替换的名称(如db_host, db_usr);然后用户必须在项目gitlab web界面(“Settings”>"CI/CD">"Variables")配置变量;最后根据gitlab-ci.yml文件定义CI/CD执行的需要,编写如下简单变量替换shell脚本;该shell脚本分别在测试环境打包阶段(beta-build)和生产环境打包阶段(prod-build)阶段运行。
+
+以下脚本仅作示例,实际应根据项目需要增加/修改需替换变量名称与对应源代码中的配置文件
+
+``` bash
+cat > .ci/config.sh << EOF
+#!/bin/bash
+
+#set -o verbose
+#set -o xtrace
+
+beta_config() {
+sed -i \
+ -e "s/db_host/$BETA_DB_HOST/g" \
+ -e "s/db_usr/$BETA_DB_USR/g" \
+ -e "s/db_pwd/$BETA_DB_PWD/g" \
+ example-web/src/main/resources/config/datasource.properties # 项目源码的配置文件
+sed -i \
+ -e "s/redis_host/$BETA_REDIS_HOST/g" \
+ -e "s/redis_port/$BETA_REDIS_PORT/g" \
+ -e "s/redis_pwd/$BETA_REDIS_PWD/g" \
+ example-web/src/main/resources/config/redis.properties # 项目源码的配置文件
+}
+
+prod_config() {
+sed -i \
+ -e "s/db_host/$PROD_DB_HOST/g" \
+ -e "s/db_usr/$PROD_DB_USR/g" \
+ -e "s/db_pwd/$PROD_DB_PWD/g" \
+ example-web/src/main/resources/config/datasource.properties
+sed -i \
+ -e "s/redis_host/$PROD_REDIS_HOST/g" \
+ -e "s/redis_port/$PROD_REDIS_PORT/g" \
+ -e "s/redis_pwd/$PROD_REDIS_PWD/g" \
+ example-web/src/main/resources/config/redis.properties
+}
+
+if [[ "$CI_JOB_STAGE" == "beta-build" ]];then
+ beta_config
+elif [[ "$CI_JOB_STAGE" == "prod-build" ]];then
+ prod_config
+else
+ echo "error: undefined CI_JOB_STAGE!"
+fi
+EOF
+```
+
diff --git a/docs/guide/gitlab/gitlab-ci.yml.md b/docs/guide/gitlab/gitlab-ci.yml.md
new file mode 100644
index 0000000..cdbe840
--- /dev/null
+++ b/docs/guide/gitlab/gitlab-ci.yml.md
@@ -0,0 +1,194 @@
+## 3.1 配置 gitlab-ci.yml
+
+示例应用搭建 CI/CD 流水线的背景需求
+
+- 应用测试环境部署在本地k8s平台,生产环境部署在阿里云上k8s平台
+- 应用的多个feature分支可以并行测试
+- 对于即将发布的release分支,本地提供封版测试环境,阿里云上提供UAT测试环境
+
+以下示例配置为个人经验总结,仅供参考,可以根据自己的理解和项目需要不断优化完善;总体来说 gitlab-ci.yml 配置很丰富,基本上能够满足各种个性化的CI/CD流程需要。
+
+``` bash
+$ cat > .ci/gitlab-ci.yml << EOF
+variables: ### 定义全局变量 http://gitlab.test.com/help/ci/variables/README.md
+ PROJECT_NS: '$CI_PROJECT_NAMESPACE-$CI_JOB_STAGE' # 定义项目命名空间,对应k8s的namespace
+ APP_NAME: '$CI_PROJECT_NAME-$CI_COMMIT_REF_SLUG' # 使用项目名和git提交信息作为应用名
+ IMAGE_NAME: '$CI_PROJECT_NAMESPACE-$CI_PROJECT_NAME:$CI_PIPELINE_ID' # 定义镜像名称
+
+stages: ### 定义ci各阶段
+ - beta-build # beta环境编译打包
+ - beta-deploy # beta环境部署
+ - beta-feature-delete # beta环境feature分支手动删除
+ - prod-build # prod环境编译打包
+ - prod-uat-deploy # prod-uat环境部署
+ - prod-deploy # prod环境部署
+ - prod-rollback # prod回滚
+
+job_beta_build:
+ stage: beta-build # beta环境编译打包
+ tags:
+ - build-shell # 定义带`build-shell`标签的runner可以运行该job
+ only: # 定义只在如下分支或者tag运行该job
+ - master
+ - develop
+ - /^feature.*$/
+ - release
+ #when: manual # 调试阶段可以先手动,后续可以注释掉以自动运行
+ script: ### runner上运行的脚本
+ - bash .ci/config.sh # 不同环境配置替换,后文详解 config.sh
+ - mvn clean install -Dmaven.test.skip=true -U # mvn 编译,可以去runner 虚机上手动执行编译测试
+ - mv example-web/target/*.jar dockerfiles/ # 把mvn生成的xxx.jar移动到dockerfiles目录下
+ - export IMAGE=`echo $IMAGE_NAME | sed 's/\//-/g'` # 转换镜像名,例:mygroup/java/example:172 >> mygroup-java-example:172
+ - cd dockerfiles && docker build -t $BETA_HARBOR/example/$IMAGE . # 创建 docker 镜像
+ - docker login -u $BETA_HARBOR_USR -p $BETA_HARBOR_PWD $BETA_HARBOR # 登陆到内部镜像仓库 harbor,并推送
+ - docker push $BETA_HARBOR/example/$IMAGE
+ - docker logout $BETA_HARBOR
+
+job_push_beta: ### 推送到beta环境,可以推送不同分支 develop, feature-1, ...>
+ stage: beta-deploy # 可以做到多分支同时测试,甚至最后的release分支也要在beta封版测试
+ tags:
+ - beta-shell # 定义带`beta-shell`标签的runner可以运行该job
+ only:
+ - master
+ - develop
+ - /^feature.*$/
+ - release
+ when: manual # 调试阶段可以先手动,后续可以注释掉以自动运行
+ variables:
+ BETA_EXP_Domain: '$CI_COMMIT_REF_SLUG.example.test.com' # job内部变量,指定该应用在beta环境的 ingress 域名
+ script:
+ - export IMAGE=`echo $IMAGE_NAME | sed 's/\//-/g'` # 转换 $IMAGE_NAME 中可能的 / 字符
+ - export PROJECT_NS=`echo $PROJECT_NS | sed 's/\//-/g'` # 转换命名空间中可能有的 / 字符
+ # 替换beta环境的参数配置
+ - sed -i "s/PROJECT_NS/$PROJECT_NS/g" .ci/app.yaml ### app.yaml 即k8s的部署模板文件,详见后面 app.yaml.md 文档,注意这里的变量有的来自>
+ - sed -i "s/APP_NAME/$APP_NAME/g" .ci/app.yaml # gitlab 系统变量, 有的是在项目 CI/CD 设置里面用户定义的变量
+ - sed -i "s/APP_REP/$BETA_APP_REP/g" .ci/app.yaml
+ - sed -i "s/AppDomain/$BETA_EXP_Domain/g" .ci/app.yaml
+ - sed -i "s/ProjectImage/$BETA_HARBOR\/example\/$IMAGE/g" .ci/app.yaml
+ - sed -i "s/DOCKER_KEY/$BETA_KEY/g" .ci/app.yaml # DOCKER_KEY 为k8s平台能从镜像仓库pull所需的认证信息,详见harbor文档
+ #
+ - mkdir -p /opt/kube/$PROJECT_NS/$APP_NAME # 在runner:beta-shell虚机本地创建应用配置目录,调试检查用
+ - cp -f .ci/app.yaml /opt/kube/$PROJECT_NS/$APP_NAME
+ - kubectl --kubeconfig=/etc/.beta/config apply -f .ci/app.yaml # 部署应用(runner虚机上预先配置了kubectl权限执行测试k8s平台)
+
+job_delete_beta: ### 多测试环境并行部署在beta k8s平台,feature分支测试完毕后删除代码分支,
+ stage: beta-feature-delete # 同时需要删除该分支在k8s平台上的部署,可以由开发人员自行执行该job删除
+ tags:
+ - beta-shell
+ only:
+ - /^feature.*$/
+ when: manual
+ script:
+ - export PROJECT_NS=`echo $PROJECT_NS | sed 's/\//-/g'`
+ - kubectl --kubeconfig=/etc/.beta/config delete deploy,svc,ing $APP_NAME -n $PROJECT_NS
+
+job_prod_build: ### prod环境编译打包,这里prod环境我们使用阿里云上的K8S
+ stage: prod-build # 阿里云k8s平台上运行的uat环境和正式环境都使用本次打包镜像
+ tags:
+ - build-shell
+ only: # 仅master和release分支可以执行该job
+ - master
+ - release
+ #when: manual
+ script:
+ - bash .ci/config.sh # config.sh 会执行替换生产环境的变量
+ - mvn clean install -Dmaven.test.skip=true -U # mvn 编译,可以去runner 虚机上手动执行编译测试
+ - mv example-web/target/*.jar dockerfiles/ # 把mvn生成的xxx.jar移动到dockerfiles目录下
+ - export IMAGE=`echo $IMAGE_NAME | sed 's/\//-/g'`
+ - cd dockerfiles && docker build -t $PROD_HARBOR/example/$IMAGE .
+ - docker login -u $PROD_HARBOR_USR -p $PROD_HARBOR_PWD $PROD_HARBOR
+ - docker push $PROD_HARBOR/example/$IMAGE
+ - docker logout $PROD_HARBOR
+
+job_push_prod_uat: ### 部署至阿里云uat环境
+ stage: prod-uat-deploy
+ tags:
+ - prod-shell
+ when: manual
+ only: # 仅master和release分支可以执行该job
+ - master
+ - release
+ variables:
+ PROD_EXP_Domain: 'example-uat.xxxx.com' # job内部变量,指定该应用在uat环境的 ingress 域名
+ script:
+ - export IMAGE=`echo $IMAGE_NAME | sed 's/\//-/g'`
+ - export PROJECT_NS=`echo $PROJECT_NS | sed 's/\//-/g'`
+ # 替换prod环境的参数配置
+ - sed -i "s/PROJECT_NS/$PROJECT_NS/g" .ci/app.yaml
+ - sed -i "s/APP_NAME/$CI_PROJECT_NAME/g" .ci/app.yaml
+ - sed -i "s/APP_REP/1/g" .ci/app.yaml
+ - sed -i "s/AppDomain/$PROD_EXP_Domain/g" .ci/app.yaml
+ - sed -i "s/ProjectImage/$PROD_HARBOR\/example\/$IMAGE/g" .ci/app.yaml
+ - sed -i "s/DOCKER_KEY/$PROD_KEY/g" .ci/app.yaml
+ #
+ - mkdir -p /opt/kube/$PROJECT_NS/$APP_NAME
+ - cp -f .ci/app.yaml /opt/kube/$PROJECT_NS/$APP_NAME
+ - kubectl --kubeconfig=/etc/.aliyun/config apply -f .ci/app.yaml
+
+job_push_prod_release: ### 部署至阿里云正式环境
+ stage: prod-deploy
+ tags:
+ - prod-shell
+ when: manual
+ only: # 仅master和release分支可以执行该job
+ - master
+ - release
+ variables:
+ PROD_EXP_Domain: 'example.xxxx.com' # 指定该应用在阿里云正式环境的 ingress 域名
+ script:
+ - export IMAGE=`echo $IMAGE_NAME | sed 's/\//-/g'`
+ - export PROJECT_NS=`echo $PROJECT_NS | sed 's/\//-/g'`
+ # 替换prod环境的参数配置
+ - sed -i "s/PROJECT_NS/$PROJECT_NS/g" .ci/app.yaml
+ - sed -i "s/APP_NAME/$CI_PROJECT_NAME/g" .ci/app.yaml
+ - sed -i "s/APP_REP/$PROD_APP_REP/g" .ci/app.yaml
+ - sed -i "s/AppDomain/$PROD_EXP_HOST/g" .ci/app.yaml
+ - sed -i "s/ProjectImage/$PROD_HARBOR\/example\/$IMAGE/g" .ci/app.yaml
+ - sed -i "s/DOCKER_KEY/$PROD_KEY/g" .ci/app.yaml
+ #
+ - mkdir -p /opt/kube/$PROJECT_NS/$APP_NAME
+ - cp -f .ci/app.yaml /opt/kube/$PROJECT_NS/$APP_NAME
+ - kubectl --kubeconfig=/etc/.aliyun/config apply -f .ci/app.yaml
+
+1/3 rollback: ### 定义生产环境回退job
+ stage: prod-rollback
+ tags:
+ - prod-shell
+ when: manual
+ only:
+ - master
+ - /^release.*$/
+ variables:
+ PROJECT_NS: '$CI_PROJECT_NAMESPACE-prod-deploy' # 定义job内变量覆盖全局变量设置
+ script:
+ - kubectl --kubeconfig=/etc/.aliyun/config -n $PROJECT_NS rollout undo deployment $CI_PROJECT_NAME --to-revision=1
+
+2/3 rollback:
+ stage: prod-rollback
+ tags:
+ - prod-shell
+ when: manual
+ only:
+ - master
+ - /^release.*$/
+ variables:
+ PROJECT_NS: '$CI_PROJECT_NAMESPACE-prod-deploy' # 定义job内变量覆盖全局变量设置
+ script:
+ - kubectl --kubeconfig=/etc/.aliyun/config -n $PROJECT_NS rollout undo deployment $CI_PROJECT_NAME --to-revision=2
+
+3/3 rollback:
+ stage: prod-rollback
+ tags:
+ - prod-shell
+ when: manual
+ only:
+ - master
+ - /^release.*$/
+ variables:
+ PROJECT_NS: '$CI_PROJECT_NAMESPACE-prod-deploy' # 定义job内变量覆盖全局变量设置
+ script:
+ - kubectl --kubeconfig=/etc/.aliyun/config -n $PROJECT_NS rollout undo deployment $CI_PROJECT_NAME --to-revision=3
+EOF
+```
+
+恭喜终于看完 gitlab-ci.yml 文件,怎么样,是不是一千个人可以写出一万个 CI/CD 流程 :)
diff --git a/docs/guide/gitlab/gitlab-install.md b/docs/guide/gitlab/gitlab-install.md
new file mode 100644
index 0000000..0e1b196
--- /dev/null
+++ b/docs/guide/gitlab/gitlab-install.md
@@ -0,0 +1,78 @@
+# 安装 gitlab
+
+gitlab 是深受企业用户喜爱的基于 git 的代码管理系统。安装 gitlab 最理想的方式是利用 gitlab charts 部署到 k8s 集群上,但此方式还未成熟,期待后续推出更成熟稳定版本;本文使用 Docker 方式安装 gitlab:
+
+- 环境:Ubuntu 16.04,虚机内存/CPU/存储请根据实际使用情况配置,一般`4C/8G/200G`足够
+- 安装 docker: 18.06.1-ce
+
+## 准备启动脚本
+
+``` bash
+$ cat > gitlab-setup.sh << EOF
+#!/bin/bash
+# 注意:设置 gitlab_shell_ssh_port 是为了后续可以使用 SSH 方式访问你的项目
+docker run --detach \\
+ --hostname gitlab.test.com \\
+ --env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.test.com/'; gitlab_rails['gitlab_shell_ssh_port'] = 6022;" \\
+ --publish 443:443 --publish 80:80 --publish 6022:22 \\
+ --name gitlab \\
+ --restart always \\
+ --volume /srv/gitlab/config:/etc/gitlab \\
+ --volume /srv/gitlab/logs:/var/log/gitlab \\
+ --volume /srv/gitlab/data:/var/opt/gitlab \\
+ registry.docker-cn.com/gitlab/gitlab-ce:11.2.2-ce.0
+EOF
+```
+执行启动脚本:`sh gitlab-setup.sh` 执行成功后,等待数分钟可以看到
+
+```
+$ docker ps -a
+CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
+4f9d5f97f494 registry.docker-cn.com/gitlab/gitlab-ce:11.2.2-ce.0 "/assets/wrapper" 9 minutes ago Up 9 minutes (healthy) 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:6022->22/tcp gitlab
+```
+## 配置 gitlab
+
+```
+$ docker exec -it gitlab vi /etc/gitlab/gitlab.rb
+```
+请阅读后修改(因为前面docker run 已经指定了必要参数,可以不修改,后续有需要再修改),修改保存以后需要重启容器
+
+```
+$ docker restart gitlab
+```
+## 首次访问 gitlab
+
+使用域名`gitlab.test.com`或者该主机 IP 首次登陆时会要求设置 root 用户的密码,完成后就可以用 root 和新设密码登陆;然后按需创建 Group, User, Projects等,还有相关配置。
+
+## 备份数据
+
+无论是企业、组织、个人都十分重视代码资产,之前我们的 gitlab 安装是单机版的,虽然可以有硬盘 raid 等保护,还有是丢失 gitlab 数据和配置的风险,因此我们有必要再做一些备份操作。这里利用 crontab 定期执行 rsync 命令备份到其他服务器。
+
+``` bash
+# 创建备份脚本
+cat > /root/gitlab-backup.sh << EOF
+#!/bin/bash
+# 请事先配置 gitlab 服务器到备份服务器的免密码 ssh 登陆
+rsync -av --delete /srv/gitlab/config '-e ssh -l root' 192.168.1.xx:/backup_gitlab/config
+rsync -av --delete /srv/gitlab/data '-e ssh -l root' 192.168.1.xx:/backup_gitlab/data
+EOF
+
+# 创建并应用 crontab
+cat > /etc/cron.d/gitlab-backup << EOF
+## 每3个小时同步备份一次,具体根据需要修改
+11 */3 * * * root bash /root/gitlab-backup.sh > /root/gitlab/sync.log 2>&1
+EOF
+```
+如果 gitlab 服务器真的出现不可恢复的故障,丢失数据,那么至少保留有3小时前的备份,利用备份的文件,同样再用 docker 挂载 volume的方式运行,这样就可以恢复原 gitlab 服务运行。
+
+## 升级 gitlab
+
+因为前面使用了 docker 方式安装,因此 gitlab 升级很方便。
+
+- 升级前停止/删除容器:`$ docker stop gitlab && docker rm gitlab`
+- 如上节执行备份数据
+- 修改 gitlab-setup.sh 指定新的版本,执行该脚本
+
+## 参考
+
+- 1.[Install GitLab with Docker](https://docs.gitlab.com/omnibus/docker/)
diff --git a/docs/guide/gitlab/gitlab-runner.md b/docs/guide/gitlab/gitlab-runner.md
new file mode 100644
index 0000000..2e08dff
--- /dev/null
+++ b/docs/guide/gitlab/gitlab-runner.md
@@ -0,0 +1,65 @@
+## 安装 Gitlab Runner
+
+Gitlab Runner 安装方式有很多,可以参考官网文档 https://docs.gitlab.com/runner/install/; 这里为了方便直接在 Ubuntu1604 上 apt方式安装了。
+
+``` bash
+$ curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
+$ apt-get install gitlab-runner
+```
+
+安装完成后就可以看到服务运行状态:`systemctl status gitlab-runner`,因为示例的java spring boot 项目需要,该虚机上要同时安装和配置 mvn 和 docker 环境。
+
+注意:需要通过 gitlab-runner shell 执行docker镜像打包等命令,因此要修改下 gitlab-runner 服务运行用户:"--user" "gitlab-runner" 改成 "--user" "root"
+
+``` bash
+vi /etc/systemd/system/gitlab-runner.service
+[Unit]
+Description=GitLab Runner
+After=syslog.target network.target
+ConditionFileIsExecutable=/usr/lib/gitlab-runner/gitlab-runner
+
+[Service]
+StartLimitInterval=5
+StartLimitBurst=10
+ExecStart=/usr/lib/gitlab-runner/gitlab-runner "run" "--working-directory" "/home/gitlab-runner" "--config" "/etc/gitlab-runner/config.toml" "--service" "gitlab-runner" "--syslog" "--user" "gitlab-runner"
+
+Restart=always
+RestartSec=120
+
+[Install]
+WantedBy=multi-user.target
+```
+
+以上配置改完保存后执行服务重启:
+
+``` bash
+$ systemctl daemon-reload
+$ systemctl restart gitlab-runner
+```
+
+### 注册 Runner
+
+运行命令`gitlab-runner register`后进入交互式界面,按照提示注册,关注下面注释内容。
+
+``` bash
+$ gitlab-runner register
+Runtime platform arch=amd64 os=linux pid=3269 revision=8bb608ff version=11.7.0
+Running in system-mode.
+
+Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
+http://gitlab.test.com/ ### 这里输入gitlab URL
+Please enter the gitlab-ci token for this runner:
+tzfBWCX-tQxxo1TCcoeJ ### 这里输入项目的token
+Please enter the gitlab-ci description for this runner:
+[k8s403]: build-shell ### 命名此runner
+Please enter the gitlab-ci tags for this runner (comma separated):
+build-shell ### 重要:指定runner tag,在gitlab-ci.yml文件中定义该tag才能执行 mvn编译/docker打包的任务
+Registering runner... succeeded runner=tzfBWCX-
+Please enter the executor: docker-ssh, parallels, shell, ssh, virtualbox, kubernetes, docker, docker+machine, docker-ssh+machine:
+shell ### 作为入门,在虚机上运行shell命令方式,方便调试
+Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
+```
+另外根据示例项目的ci/cd流程,还需要注册标签 tag 为 `beta-shell` 和 `prod-shell` 的两个 Runner; 注意这两个runner所在虚机需要分别配置测试k8s和生产k8s的 kubeconfig 配置,这样 Runner 才能通过 shell 脚本执行 kubectl apply 命令部署应用。三个 Runner 注册成功后可以看到如图:
+
+
+
diff --git a/docs/guide/gitlab/pics/active-runner.jpg b/docs/guide/gitlab/pics/active-runner.jpg
new file mode 100644
index 0000000..e4782db
Binary files /dev/null and b/docs/guide/gitlab/pics/active-runner.jpg differ
diff --git a/docs/guide/gitlab/pics/cicd-pipeline.jpg b/docs/guide/gitlab/pics/cicd-pipeline.jpg
new file mode 100644
index 0000000..4aab215
Binary files /dev/null and b/docs/guide/gitlab/pics/cicd-pipeline.jpg differ
diff --git a/docs/guide/gitlab/pics/cicd-setting.jpg b/docs/guide/gitlab/pics/cicd-setting.jpg
new file mode 100644
index 0000000..4cfa465
Binary files /dev/null and b/docs/guide/gitlab/pics/cicd-setting.jpg differ
diff --git a/docs/guide/gitlab/pics/runner.jpg b/docs/guide/gitlab/pics/runner.jpg
new file mode 100644
index 0000000..fc464d2
Binary files /dev/null and b/docs/guide/gitlab/pics/runner.jpg differ
diff --git a/docs/guide/gitlab/readme.md b/docs/guide/gitlab/readme.md
new file mode 100644
index 0000000..f2b5dd5
--- /dev/null
+++ b/docs/guide/gitlab/readme.md
@@ -0,0 +1,139 @@
+# Gitlab CI/CD 基础
+
+Gitlab-ci 兼容 travis ci 格式,也是最流行的 CI 工具之一;本文讲解利用 gitlab, gitlab-runner, docker, harbor, kubernetes 等流行开源工具搭建一个自动化CI/CD流水线;示例配置以简单实用为原则,暂时没有选用 dind(docker in dockers)打包、gitlab Auto DevOps 等方式。一个最简单的流水线如下:
+
+- 代码提交 --> 镜像构建 --> 部署测试 --> 部署生产
+
+## 0.前提条件
+
+- 正常运行的 gitlab,[安装 gitlab 文档](gitlab-install.md)
+- 正常运行的容器仓库,[安装 Harbor 文档](../harbor.md)
+- 正常运行的 k8s,可以本地自建 k8s 集群,也可以使用公有云 k8s 集群
+- 若干虚机运行 gitlab-runner: 运行自动化流水线任务 pipeline job
+- 了解代码管理流程 gitflow 等
+
+## 1.准备测试项目代码
+
+假设你要开发一个 spring boot 项目;先登陆你的 gitlab 账号,创建项目,上传你的代码;项目根目录看起来如下:
+
+```
+-rw-r--r-- 1 root root 44 Jan 2 16:38 eclipse.bat
+drwxr-xr-x 8 root root 4096 Jan 7 15:29 .git/
+-rw-r--r-- 1 root root 276 Jan 7 08:44 .gitignore
+drwxr-xr-x 3 root root 4096 Jan 7 08:44 example-api/
+drwxr-xr-x 3 root root 4096 Jan 7 08:44 example-biz/
+drwxr-xr-x 3 root root 4096 Jan 2 16:38 example-dal/
+drwxr-xr-x 3 root root 4096 Jan 2 16:38 example-web/
+-rw-r--r-- 1 root root 54 Jan 2 16:38 install.bat
+-rw-r--r-- 1 root root 10419 Jan 2 16:38 pom.xml
+```
+传统做法是在本地配置好相关环境后使用 mvn 编译生成jar包,然后测试运行jar;这里我们要把应用打包成 docker 镜像,并创建 CI/CD 流水线:如下示例,在项目根目录新增创建2个文件夹及相关文件
+
+``` bash
+dockerfiles ### 新增文件夹用来 docker 镜像打包
+└── Dockerfile # 定义 docker 镜像
+.ci ### 新增文件夹用来存放 CI/CD 相关内容
+├── app.yaml # k8s 平台的应用部署文件
+├── config.sh # 配置替换脚本
+└── gitlab-ci.yml # gitlab-ci 的主配置文件
+```
+
+## 2.准备 docker 镜像描述文件 Dockerfile
+
+我们把 Dockerfile 放在独立目录下,java spring boot 应用可以这样写:
+
+``` bash
+cat > dockerfiles/Dockerfile << EOF
+FROM openjdk:8-jdk-alpine
+VOLUME /tmp
+COPY *.jar app.jar # 这里 *.jar 包就是后续在cicd pipeline 过程中 mvn 生成的jar包移动到此目录
+ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
+EOF
+```
+
+## 3.准备 CI/CD 相关脚本和文件
+
+装完 gitlab 后使用浏览器登陆gitlab,很容易找到帮助文档,里面有介绍gitlab-ci的内容(文档权威、详细!请多多阅读~ 随着CI/CD流程的深入,部分内容也可以回来查阅),先看如下文档(假设你本地gitlab使用域名`gitlab.test.com`)
+
+- 文档首页 http://gitlab.test.com/help
+- gitlab-ci 基本概念 http://gitlab.test.com/help/ci/README.md
+- variables 变量 http://gitlab.test.com/help/ci/variables/README.md
+
+目录`.ci`下面的三个文件`app.yaml`, `config.sh`, `gitlab-ci.yml`是互相关联的;gitlab-ci.yml 文件中会调用到另外两个文件;文件之间又通过一些变量定义联系,流程中用到的变量大致可以分为三种:
+
+- 第一种是gitlab自身预定义变量(比如项目名: CI_PROJECT_NAME,流水线ID: CI_PIPELINE_ID);无需更改;
+- 第二种是在gitlab-ci.yml文件中定义的变量,一般是少量的自定义变量;按需少量改动;
+- 第三种是用户可以在项目web界面配置的变量:“Settings”>"CI/CD">"Variables",本示例项目用到该类型变量举例:
+
+|变量|值|注解|
+|:-|:-|:-|
+|BETA_APP_REP|1|beta环境应用副本数|
+|BETA_DB_HOST|1.1.1.1:3306|beta环境应用连接数据库主机|
+|BETA_DB_PWD|xxxx|beta环境数据库连接密码|
+|BETA_DB_USR|xxxx|beta环境数据库连接用户|
+|BETA_REDIS_HOST|1.1.1.2|beta环境redis主机|
+|BETA_REDIS_PORT|6379|beta环境redis端口|
+|BETA_REDIS_PWD|xxxx|beta环境redis密码|
+|BETA_HARBOR|1.1.1.3|beta环境镜像仓库地址|
+|BETA_HARBOR_PWD|xxxx|beta环境镜像仓库密码|
+|BETA_HARBOR_USR|xxxx|beta环境镜像仓库用户|
+|PROD_APP_REP|2|prod环境应用副本数|
+|PROD_DB_HOST|2.2.2.1:3306|prod环境应用连接数据库主机|
+|PROD_DB_PWD|xxxx|prod环境数据库连接密码|
+|PROD_DB_USR|xxxx|prod环境数据库连接用户|
+|PROD_REDIS_HOST|2.2.2.2|prod环境redis主机|
+|PROD_REDIS_PORT|6379|prod环境redis端口|
+|PROD_REDIS_PWD|xxxx|prod环境redis密码|
+|PROD_HARBOR|2.2.2.3|prod环境镜像仓库地址|
+|PROD_HARBOR_PWD|xxxx|prod环境镜像仓库密码|
+|PROD_HARBOR_USR|xxxx|prod环境镜像仓库用户|
+|...|...|根据项目需要自行添加设置|
+
+掌握了以上基础知识,可以开始以下三个任务:
+
+- 3.1[配置 gitlab-ci.yml](gitlab-ci.yml.md), 整个CI/CD的主配置文件,定义所有的CI/CD阶段和每个阶段的任务
+- 3.2[配置 config.sh](config.sh.md),根据不同分支/环境替换不同的应用程序变量(对应上述第三种变量)
+- 3.3[配置 app.yaml](app.yaml.md),K8S应用部署简单模板,替换完成后可以部署到测试/生产的K8S平台上
+
+## 4.为项目配置 CI/CD 及创建 RUNNER
+
+使用浏览器访问gitlab,登陆后,在项目页面进行配置,如图:
+
+
+
+- 在 General pipelines 中配置 Custom CI config path 为 .ci/gitlab-ci.yml
+- 在 Variables 中配置需要用到的变量
+- 在 Runners 中配置注册 gitlab-runner 实例(runner 就是用来自动执行ci job的),点进去后如图:
+
+
+
+- 作为入门,先来手动创建 specific Runner,后续同样可以创建 Group Runners/Shared Runners,使用起来更方便;本文档暂不涉及在 kubernetes 自动创建 Runner
+ - 按照官网文档安装 Gitlab Runner,参考[文档](gitlab-runner.md)
+ - 记下 gitlab URL, 项目 token,注册 Runner 时要用到
+ - 在 Gitlab Runner 注册本项目
+
+## 5.提交代码测试 CI/CD Pipelines
+
+终于经过 1~4 步骤把示例项目的CI/CD 流水线创建出来了,是时候试试提交代码测试下成果了;别担心,初次 CI/CD job执行一般都会失败的:) 好在现在你已经基本了解了所有CI/CD流程与配置,失败了就查看错误日志一一排除。另外因为采用虚机安装 Runner 执行 shell 脚本的方式执行 ci job,我们始终可以登陆虚机以手动执行 shell 脚本的方式调试,这对于初学来说很有帮助。查看 CI/CD 执行情况如图:
+
+
+
+## 6.gitlab-ci 安全实践
+
+现在为止 CICD Pipelines 已经可以跑通了,甚至稍微修改下 gitlab-ci.yml 配置,项目代码每一次提交后可以自动执行`编译`、`打包`、`部署测试`、`部署生产`等等工作;也许你还没来得及慢慢体会这顺畅的感觉,赶紧先踩个刹车,控制下车速;因为现在你需要考虑 gitlab-ci 的安全配置了,这很重要!
+
+首先 gitlab 项目的基本安全就是项目成员控制,访问项目的权限分为:所有者(Owner),维护者(Maintainer),开发者(Developer),报告者(Reporter),访客(Guest);详细的权限介绍请查阅官方文档,这里简单地介绍两类权限:所有者和维护者属于`特权用户`,开发者属于`普通用户`,他们应该具有如下权限区分:
+
+- 特权用户对整个项目负责,包括项目代码开发、配置管理、CI流程、测试环境、生产环境等
+- 特权用户可以提交代码到所有分支包括 master/release 分支,执行所有 ci job
+- 普通用户只负责对应项目模块代码开发、不接触程序配置、只能访问测试环境
+- 普通用户只能提交代码到 develop/feature 分支,只能执行这两个分支的 ci job
+
+以下的安全实践配置作为个人经验分享,仅作参考;如果你的项目需要更高的安全性,请阅读 gitlab-ci 官方相关文档,尝试找到属于自己的最佳实践。
+
+- 正确设置项目成员(Settings > Members),严格限制项目维护者(Maintainer)人数,大部分应该作为开发者(Developer)提交代码
+- 配置项目受保护分支/受保护标签,一般把master/release分支设置成受保护分支,限制只有维护者才能在保护分支commit和merge,从而限制只有维护者才能执行部署生产的 ci job,http://gitlab.test.com/help/user/project/protected_branches.md
+- 配置受保护的变量,受保护的变量只在受保护分支和受保护tag的pipeline中可见,防止生产环境配置参数泄露,http://gitlab.test.com/help/ci/variables/README#protected-variables
+- 配置受保护的Runner,只能执行受保护分支上的 ci jobs
+- CICD Pipelines 中发布生产的任务请设置手动执行,同样生产的回退任务设置手动执行
+
diff --git a/docs/guide/harbor.md b/docs/guide/harbor.md
new file mode 100644
index 0000000..131b7bc
--- /dev/null
+++ b/docs/guide/harbor.md
@@ -0,0 +1,175 @@
+## harbor
+
+Habor是由VMWare中国团队开源的容器镜像仓库。事实上,Habor是在Docker Registry上进行了相应的企业级扩展,从而获得了更加广泛的应用,这些新的企业级特性包括:管理用户界面,基于角色的访问控制 ,水平扩展,同步,AD/LDAP集成以及审计日志等。本文档仅说明部署单个基础harbor服务的步骤。
+
+### 安装步骤
+
+1. 在deploy节点下载最新的 [docker-compose](https://github.com/docker/compose/releases) 二进制文件,改名后把它放到项目 `/etc/ansible/bin`目录下(百度云的二进制文件中已包含)
+
+``` bash
+wget https://github.com/docker/compose/releases/download/1.18.0/docker-compose-Linux-x86_64
+mv docker-compose-Linux-x86_64 /etc/ansible/bin/docker-compose
+```
+2. 在deploy节点下载最新的 [harbor](https://github.com/vmware/harbor/releases) 离线安装包,把它放到项目 `/etc/ansible/down` 目录下,也可以从分享的百度云盘下载
+
+3. 在deploy节点编辑/etc/ansible/hosts文件,可以参考 `example`目录下的模板,修改部分举例如下
+
+``` bash
+# 参数 NEW_INSTALL=(yes/no):yes表示新建 harbor,并配置k8s节点的docker可以使用harbor仓库
+# no 表示仅配置k8s节点的docker使用已有的harbor仓库
+# 如果不需要设置域名访问 harbor,可以配置参数 HARBOR_DOMAIN=""
+[harbor]
+192.168.1.8 HARBOR_DOMAIN="harbor.yourdomain.com" NEW_INSTALL=yes
+```
+
+4. 在deploy节点执行 `ansible-playbook /etc/ansible/11.harbor.yml`,完成harbor安装和docker 客户端配置
+
+### 安装讲解
+
+根据 `11.harbor.yml`文件,harbor节点需要以下步骤:
+
+1. role `prepare` 基础系统环境准备
+1. role `docker` 安装docker
+1. role `harbor` 安装harbor
+
+`kube-node`节点在harbor部署完之后,需要配置harbor的证书,并可以在hosts里面添加harbor的域名解析,如果你的环境中有dns服务器,可以跳过hosts文件设置
+
+请在另外窗口打开 [roles/harbor/tasks/main.yml](../../roles/harbor/tasks/main.yml),对照以下讲解
+
+1. 下载docker-compose可执行文件到$PATH目录
+1. 自注册变量result判断是否已经安装harbor,避免重复安装问题
+1. 解压harbor离线安装包到指定目录
+1. 导入harbor所需 docker images
+1. 创建harbor证书和私钥(复用集群的CA证书)
+1. 修改harbor.cfg配置文件
+1. 启动harbor安装脚本
+
+### 验证harbor
+
+1. 在harbor节点使用`docker ps -a` 查看harbor容器组件运行情况
+1. 浏览器访问harbor节点的IP地址 `https://$NodeIP`,使用账号 admin 和 密码 Harbor12345 (harbor.cfg 配置文件中的默认)登陆系统
+
+### 在k8s集群使用harbor
+
+admin用户web登陆后可以方便的创建项目,并指定项目属性(公开或者私有);然后创建用户,并在项目`成员`选项中选择用户和权限;
+
+#### 镜像上传
+
+在node上使用harbor私有镜像仓库首先需要在指定目录配置harbor的CA证书,详见 `11.harbor.yml`文件。
+
+使用docker客户端登陆`harbor.test.com`,然后把镜像tag成 `harbor.test.com/$项目名/$镜像名:$TAG` 之后,即可使用docker push 上传
+
+``` bash
+docker login harbor.test.com
+Username:
+Password:
+Login Succeeded
+docker tag busybox:latest harbor.test.com/library/busybox:latest
+docker push harbor.test.com/library/busybox:latest
+The push refers to a repository [harbor.test.com/library/busybox]
+0271b8eebde3: Pushed
+latest: digest: sha256:91ef6c1c52b166be02645b8efee30d1ee65362024f7da41c404681561734c465 size: 527
+```
+#### k8s中使用harbor
+
+1. 如果镜像保存在harbor中的公开项目中,那么只需要在yaml文件中简单指定harbor私有镜像即可,例如
+
+``` bash
+apiVersion: v1
+kind: Pod
+metadata:
+ name: test-busybox
+spec:
+ containers:
+ - name: test-busybox
+ image: harbor.test.com/xxx/busybox:latest
+ imagePullPolicy: Always
+```
+
+2. 如果镜像保存在harbor中的私有项目中,那么yaml文件中使用该私有项目的镜像需要指定`imagePullSecrets`,例如
+
+``` bash
+apiVersion: v1
+kind: Pod
+metadata:
+ name: test-busybox
+spec:
+ containers:
+ - name: test-busybox
+ image: harbor.test.com/xxx/busybox:latest
+ imagePullPolicy: Always
+ imagePullSecrets:
+ - name: harborkey1
+```
+其中 `harborKey1`可以用以下两种方式生成:
+
++ 1.使用 `kubectl create secret docker-registry harborkey1 --docker-server=harbor.test.com --docker-username=admin --docker-password=Harbor12345 --docker-email=team@test.com`
++ 2.使用yaml配置文件生成
+
+``` bash
+//harborkey1.yaml
+apiVersion: v1
+kind: Secret
+metadata:
+ name: harborkey1
+ namespace: default
+data:
+ .dockerconfigjson: {base64 -w 0 ~/.docker/config.json}
+type: kubernetes.io/dockerconfigjson
+```
+前面docker login会在~/.docker下面创建一个config.json文件保存鉴权串,这里secret yaml的.dockerconfigjson后面的数据就是那个json文件的base64编码输出(-w 0让base64输出在单行上,避免折行)
+
+### 管理harbor
+
++ 日志目录 `/var/log/harbor`
++ 数据目录 `/data` ,其中最主要是 `/data/database` 和 `/data/registry` 目录,如果你要彻底重新安装harbor,删除这两个目录即可
+
+先进入harbor安装目录 `cd /data/harbor`,常规操作如下:
+
+1. 暂停harbor `docker-compose stop` : docker容器stop,并不删除容器
+2. 恢复harbor `docker-compose start` : 恢复docker容器运行
+3. 停止harbor `docker-compose down -v` : 停止并删除docker容器
+4. 启动harbor `docker-compose up -d` : 启动所有docker容器
+
+修改harbor的运行配置,需要如下步骤:
+
+``` bash
+# 停止 harbor
+ docker-compose down -v
+# 修改配置
+ vim harbor.cfg
+# 执行./prepare已更新配置到docker-compose.yml文件
+ ./prepare
+# 启动 harbor
+ docker-compose up -d
+```
+#### harbor 升级
+
+以下步骤基于harbor 1.1.2 版本升级到 1.2.2版本
+
+``` bash
+# 进入harbor解压缩后的目录,停止harbor
+cd /data/harbor
+docker-compose down
+
+# 备份这个目录
+cd ..
+mkdir -p /backup && mv harbor /backup/harbor
+
+# 下载更新的离线安装包,并解压
+tar zxvf harbor-offline-installer-v1.2.2.tgz -C /data
+
+# 使用官方数据库迁移工具,备份数据库,修改数据库连接用户和密码,创建数据库备份目录
+# 迁移工具使用docker镜像,镜像tag由待升级到目标harbor版本决定,这里由 1.1.2升级到1.2.2,所以使用 tag 1.2
+docker pull vmware/harbor-db-migrator:1.2
+mkdir -p /backup/db-1.1.2
+docker run -it --rm -e DB_USR=root -e DB_PWD=xxxx -v /data/database:/var/lib/mysql -v /backup/db-1.1.2:/harbor-migration/backup vmware/harbor-db-migrator:1.2 backup
+
+# 因为新老版本数据库结构不一样,需要数据库migration
+docker run -it --rm -e DB_USR=root -e DB_PWD=xxxx -v /data/database:/var/lib/mysql vmware/harbor-db-migrator:1.2 up head
+
+# 修改新版本 harbor.cfg配置,需要保持与老版本相关配置项保持一致,然后执行安装即可
+cd /data/harbor
+vi harbor.cfg
+./install.sh
+```
diff --git a/docs/guide/heapster.1.4.3.md b/docs/guide/heapster.1.4.3.md
new file mode 100644
index 0000000..982c72e
--- /dev/null
+++ b/docs/guide/heapster.1.4.3.md
@@ -0,0 +1,91 @@
+## heapster
+
+`Heapster` 监控整个集群资源的过程:首先kubelet内置的cAdvisor收集本node节点的容器资源占用情况,然后heapster从kubelet提供的api采集节点和容器的资源占用,最后heapster 持久化数据存储到`influxdb`中(也可以是其他的存储后端,Google Cloud Monitoring等)。
+
+`Grafana` 则通过配置数据源指向上述 `influxdb`,从而界面化显示监控信息。
+
+### 部署
+
+访问 [heapster release](https://github.com/kubernetes/heapster)页面下载最新 release 1.4.3,参考目录`heapster-1.3.0/deploy/kube-config/influxdb`,因为这个官方release 在k8s1.8.4使用还是有不少问题,请在参考的基础上使用本项目提供的yaml文件
+
+1. [grafana](../../manifests/heapster/grafana.yaml)
+1. [heapster](../../manifests/heapster/heapster.yaml)
+1. [influxdb](../../manifests/heapster/influxdb.yaml)
+
+安装比较简单 `kubectl create -f /etc/ansible/manifests/heapster/`,主要讲一下注意事项
+
+#### grafana.yaml配置
+
++ 修改`heapster-grafana-amd64`镜像,v4.2.0版本修改成 v4.4.3版本,否则 grafana pod无法起来,报`CrashLoopBackOff`错误,详见[ISSUE](https://github.com/kubernetes/heapster/issues/1806)
++ 参数`- name: GF_SERVER_ROOT_URL`的设置要根据后续访问grafana的方式确定,如果使用 NodePort方式访问,必须设置成:`value: /`;如果使用apiserver proxy方式,必须设置成`value: /api/v1/namespaces/kube-system/services/monitoring-grafana/proxy/`,注意官方文件中预设的`value: /api/v1/proxy/namespaces/kube-system/services/monitoring-grafana/`已经不适合k8s 1.8.0版本了,
++ `kubernetes.io/cluster-service: 'true'` 和 `type: NodePort` 根据上述的访问方式设置,建议使用apiserver 方式,可以增加安全控制
+
+#### heapster.yaml配置
+
++ 需要配置 RBAC 把 ServiceAccount `heapster` 与集群预定义的集群角色 `system:heapster` 绑定,这样heapster pod才有相应权限去访问 apiserver
+
+#### influxdb.yaml配置
+
++ influxdb 官方建议使用命令行或 HTTP API 接口来查询数据库,从 v1.1.0 版本开始默认关闭 admin UI,这里参考[opsnull](https://github.com/opsnull/follow-me-install-kubernetes-cluster/blob/master/10-%E9%83%A8%E7%BD%B2Heapster%E6%8F%92%E4%BB%B6.md)给出的方法,增加ConfigMap配置,然后挂载到容器中,覆盖默认配置
++ 注意influxdb 这个版本只能使用 NodePort方式访问它的admin UI,才能正确连接数据库
+
+### 验证
+
+``` bash
+$ kubectl get pods -n kube-system | grep -E 'heapster|monitoring'
+heapster-3273315324-tmxbg 1/1 Running 0 11m
+monitoring-grafana-2255110352-94lpn 1/1 Running 0 11m
+monitoring-influxdb-884893134-3vb6n 1/1 Running 0 11m
+```
+扩展检查Pods日志:
+``` bash
+$ kubectl logs heapster-3273315324-tmxbg -n kube-system
+$ kubectl logs monitoring-grafana-2255110352-94lpn -n kube-system
+$ kubectl logs monitoring-influxdb-884893134-3vb6n -n kube-system
+```
+部署完heapster,使用上一步介绍方法查看kubernets dashboard 界面,就可以看到各 Nodes、Pods 的 CPU、内存、负载等利用率曲线图,如果 dashboard上还无法看到利用率图,使用以下命令重启 dashboard pod:
++ 首先删除 `kubectl scale deploy kubernetes-dashboard --replicas=0 -n kube-system`
++ 然后新建 `kubectl scale deploy kubernetes-dashboard --replicas=1 -n kube-system`
+
+### 访问 grafana
+
+#### 1.通过apiserver 访问(建议的方式)
+
+``` bash
+kubectl cluster-info | grep grafana
+monitoring-grafana is running at https://x.x.x.x:6443/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
+```
+请参考上一步 [访问dashboard](dashboard.md)同样的方式,使用证书或者密码认证(参照hosts文件配置,默认:用户admin 密码test1234),访问`https://x.x.x.x:6443/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy`即可,如图可以点击[Home]选择查看 `Cluster` `Pods`的监控图形
+
+
+
+#### 2.通过NodePort 访问
+
++ 修改 `Service` 允许 type: NodePort
++ 修改 `Deployment`中参数`- name: GF_SERVER_ROOT_URL`为 `value: /`
++ 如果之前grafana已经运行,使用 `kubectl replace --force -f /etc/ansible/manifests/heapster/grafana.yaml` 重启 grafana插件
+
+``` bash
+kubectl get svc -n kube-system|grep grafana
+monitoring-grafana NodePort 10.68.135.50 80:5855/TCP 11m
+```
+然后用浏览器访问 http://NodeIP:5855
+
+### 访问 influxdb
+
+官方建议使用命令行或 HTTP API 接口来查询`influxdb`数据库,如非必要就跳过此步骤
+
+目前根据测试 k8s v1.8.4 使用 NodePort 方式访问 admin 界面后才能正常连接数据库
+
+``` bash
+kubectl get svc -n kube-system|grep influxdb
+monitoring-influxdb NodePort 10.68.195.193 8086:3382/TCP,8083:7651/TCP 12h
+```
++ 如上例子,8083是管理页面端口,对外暴露的端口为7651
++ 8086 是数据连接端口,对外暴露的端口为3382
+
+使用浏览器访问 http://NodeIP:7651,如图在页面的 “Connection Settings” 的 Host 中输入 node IP, Port 中输入 3382(由8086对外暴露的端口),点击 “Save” 即可
+
+
+
+
diff --git a/docs/guide/heapster.md b/docs/guide/heapster.md
new file mode 100644
index 0000000..a20e7e2
--- /dev/null
+++ b/docs/guide/heapster.md
@@ -0,0 +1,167 @@
+## 第一部分:heapster
+
++ 本文档基于heapster 1.5.1和k8s 1.9.x,旧版文档请看[heapster 1.4.3](heapster.1.4.3.md)
+
+`Heapster` 监控整个集群资源的过程:首先kubelet内置的cAdvisor收集本node节点的容器资源占用情况,然后heapster从kubelet提供的api采集节点和容器的资源占用,最后heapster 持久化数据存储到`influxdb`中(也可以是其他的存储后端,Google Cloud Monitoring等)。
+
+`Grafana` 则通过配置数据源指向上述 `influxdb`,从而界面化显示监控信息。
+
+### 部署
+
+访问 [heapster release](https://github.com/kubernetes/heapster)页面下载最新 release 1.5.1,参考目录`heapster-1.5.1/deploy/kube-config/influxdb`,请在参考官方yaml文件的基础上使用本项目提供的yaml文件
+
+1. [grafana](../../manifests/heapster/grafana.yaml)
+1. [heapster](../../manifests/heapster/heapster.yaml)
+1. [influxdb](../../manifests/heapster/influxdb.yaml)
+
+安装比较简单 `kubectl create -f /etc/ansible/manifests/heapster/`,主要讲一下注意事项
+
+#### grafana.yaml配置
+
++ 参数`- name: GF_SERVER_ROOT_URL`的设置要根据后续访问grafana的方式确定,如果使用 NodePort方式访问,必须设置成:`value: /`;如果使用apiserver proxy方式,必须设置成`value: /api/v1/namespaces/kube-system/services/monitoring-grafana/proxy/`
++ `kubernetes.io/cluster-service: 'true'` 和 `type: NodePort` 根据上述的访问方式设置,建议使用apiserver 方式,可以增加安全控制
+
+#### heapster.yaml配置
+
++ 需要配置 RBAC 把 ServiceAccount `heapster` 与集群预定义的集群角色 `system:heapster` 绑定,这样heapster pod才有相应权限去访问 apiserver
+
+#### influxdb.yaml配置
+
++ influxdb 官方建议使用命令行或 HTTP API 接口来查询数据库,从 v1.1.0 版本开始默认关闭 admin UI, 从 v1.3.3 版本开始已经移除 admin UI 插件,如果你因特殊原因需要访问admin UI,请使用 v1.1.1 版本并使用configMap 配置开启它。参考[heapster 1.4.3](heapster.1.4.3.md),具体配置yaml文件参考[influxdb v1.1.1](../../manifests/heapster/influxdb-v1.1.1/influxdb.yaml)
+
+### 验证
+
+``` bash
+$ kubectl get pods -n kube-system | grep -E 'heapster|monitoring'
+heapster-3273315324-tmxbg 1/1 Running 0 11m
+monitoring-grafana-2255110352-94lpn 1/1 Running 0 11m
+monitoring-influxdb-884893134-3vb6n 1/1 Running 0 11m
+```
+检查Pods日志:
+``` bash
+$ kubectl logs heapster-3273315324-tmxbg -n kube-system
+$ kubectl logs monitoring-grafana-2255110352-94lpn -n kube-system
+$ kubectl logs monitoring-influxdb-884893134-3vb6n -n kube-system
+```
+部署完heapster,使用上一步介绍方法查看kubernets dashboard 界面,就可以看到各 Nodes、Pods 的 CPU、内存、负载等利用率曲线图,如果 dashboard上还无法看到利用率图,使用以下命令重启 dashboard pod:
++ 首先删除 `kubectl scale deploy kubernetes-dashboard --replicas=0 -n kube-system`
++ 然后新建 `kubectl scale deploy kubernetes-dashboard --replicas=1 -n kube-system`
+
+部署完heapster,直接使用 `kubectl` 客户端工具查看资源使用
+
+``` bash
+# 查看node 节点资源使用情况
+$ kubectl top node
+# 查看各pod 的资源使用情况
+$ kubectl top pod --all-namespaces
+```
+
+### 访问 grafana
+
+#### 1.通过apiserver 访问(建议的方式)
+
+``` bash
+kubectl cluster-info | grep grafana
+monitoring-grafana is running at https://x.x.x.x:6443/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
+```
+请参考上一步 [访问dashboard](dashboard.md)同样的方式,使用证书或者密码认证(参照hosts文件配置,默认:用户admin 密码test1234),访问`https://x.x.x.x:6443/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy`即可,如图可以点击[Home]选择查看 `Cluster` `Pods`的监控图形
+
+
+
+#### 2.通过NodePort 访问
+
++ 修改 `Service` 允许 type: NodePort
++ 修改 `Deployment`中参数`- name: GF_SERVER_ROOT_URL`为 `value: /`
++ 如果之前grafana已经运行,使用 `kubectl replace --force -f /etc/ansible/manifests/heapster/grafana.yaml` 重启 grafana插件
+
+``` bash
+kubectl get svc -n kube-system|grep grafana
+monitoring-grafana NodePort 10.68.135.50 80:5855/TCP 11m
+```
+然后用浏览器访问 http://NodeIP:5855
+
+
+## 第二部分:heapster 之监控数据持久化
+
+我们知道监控数据是存储到`influxdb`中的,但是默认情况下`influxdb.yaml`文件中存储使用的是`emptyDir`类型,所以当influxdb POD被删除时,监控数据也就丢失了,以下是使用nfs持久化保存监控数据的例子。
+
+### 前提
+环境准备一个nfs服务器,如果没有可以参考[nfs-server](nfs-server.md)创建。
+
+### 创建 PV
+PersistentVolume (PV) 和 PersistentVolumeClaim (PVC) 提供了方便的持久化卷;`PV` 是集群的存储资源,就像 `node`是集群的计算资源,PV 可以静态或动态创建,这里使用静态方式创建;`PVC` 就是用来申请`PV` 资源,它可以直接挂载在`POD` 里面使用。更多知识请访问[官网](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims)。根据你监控日志的多少和需保存时间需求创>建固定大小的`PV` 资源,例子:
+
+``` bash
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: pv-influxdb
+spec:
+ capacity:
+ storage: 5Gi
+ accessModes:
+ - ReadWriteMany
+ volumeMode: Filesystem
+ persistentVolumeReclaimPolicy: Recycle
+ storageClassName: slow
+ nfs:
+ # 根据实际共享目录修改
+ path: /share
+ # 根据实际 nfs服务器地址修改
+ server: 192.168.1.208
+```
+
+### 修改influxdb 存储卷
+使用PVC 替换 volumes `emptyDir:{}`,创建PVC 如下:
+
+``` bash
+kind: PersistentVolumeClaim
+apiVersion: v1
+metadata:
+ name: influxdb-claim
+ namespace: kube-system
+spec:
+ accessModes:
+ - ReadWriteMany
+ volumeMode: Filesystem
+ resources:
+ requests:
+ storage: 3Gi
+ storageClassName: slow
+```
++ 注意`PV` 是不区分namespace,而`PVC` 是区分namespace的
+
+### 安装持久化influxdb
+
+如果之前已经安装本项目创建了`heapster`,请使用如下删除 `influxdb POD`:
+
+``` bash
+kubectl delete -f /etc/ansible/manifests/heapster/influxdb.yaml
+```
+
+然后使用如下命令新建持久化的 `influxdb POD` :
+
+``` bash
+kubectl create -f /etc/ansible/manifests/heapster/influxdb-with-pv/
+```
+
+### 验证监控数据的持久性
+
++ 1.查看集群 `pv` `pvc` 情况
+
+``` bash
+$ kubectl get pv
+$ kubectl get pvc --all-namespaces
+```
+
++ 2.手动删除 `influxdb`,半小时后再次创建,登陆grafana 确认历史数据是否还在。
+
+``` bash
+# 删除 influxdb deploy
+kubectl delete -f /etc/ansible/manifests/heapster/influxdb-with-pv/influxdb.yaml
+
+# 等待半小时后重新创建
+kubectl create -f /etc/ansible/manifests/heapster/influxdb-with-pv/influxdb.yaml
+```
+
+
diff --git a/docs/guide/helm.md b/docs/guide/helm.md
new file mode 100644
index 0000000..e221b01
--- /dev/null
+++ b/docs/guide/helm.md
@@ -0,0 +1,58 @@
+# Helm
+
+`Helm`致力于成为k8s集群的应用包管理工具,希望像linux 系统的`RPM` `DPKG`那样成功;确实在k8s上部署复杂一点的应用很麻烦,需要管理很多yaml文件(configmap,controller,service,rbac,pv,pvc等等),而helm能够整齐管理这些文档:版本控制,参数化安装,方便的打包与分享等。
+- 建议积累一定k8s经验以后再去使用helm;对于初学者来说手工去配置那些yaml文件对于快速学习k8s的设计理念和运行原理非常有帮助,而不是直接去使用helm,面对又一层封装与复杂度。
+- 本文参考 helm 官网安全实践启用 TLS 认证,参考 https://docs.helm.sh/using_helm/#securing-your-helm-installation
+
+## 安全安装 helm(在线)
+
+在helm客户端和tiller服务器间建立安全的SSL/TLS认证机制;tiller服务器和helm客户端都是使用同一CA签发的`client cert`,然后互相识别对方身份。建议通过本项目提供的`ansible role`安装,符合官网上介绍的安全加固措施,在delpoy节点运行:
+``` bash
+# 1.如果已安装非安全模式,使用 helm reset 清理
+# 2.配置默认helm参数 vi /etc/ansible/roles/helm/defaults/main.yml
+# 3.执行安装
+$ ansible-playbook /etc/ansible/roles/helm/helm.yml
+```
+
+简单介绍下`/roles/helm/tasks/main.yml`中的步骤
+
+- 1-下载最新release的helm客户端到/etc/ansible/bin目录下,再由它自动推送到deploy的{{ bin_dir }}目录下
+- 2-由集群CA签发helm客户端证书和私钥
+- 3-由集群CA签发tiller服务端证书和私钥
+- 4-创建tiller专用的RBAC配置,只允许helm在指定的namespace查看和安装应用
+- 5-安全安装tiller到集群,tiller服务启用tls验证
+- 6-配置helm客户端使用tls方式与tiller服务端通讯
+
+### 注意因使用了TLS认证,所以helm命令执行分以下两种情况
+
+- 执行与tiller服务有关的命令,比如 `helm ls` `helm version` `helm install`等需要加`--tls`参数
+- 执行其他命令,比如`helm search` `helm fetch` `helm home`等不需要加`--tls`
+- helm v2.11.0及以上版本,启用环境变量 HELM_TLS_ENABLE=true,可以都不用加 --tls 参数
+
+## 安全安装 helm(离线)
+在内网环境中,由于不能访问互联网,无法连接repo地址,使用上述的在线安装helm的方式会报错。因此需要使用离线安装的方法来安装。
+离线安装步骤:
+```bash
+# 1.创建本地repo
+mkdir -p /opt/helm-repo
+# 2.启动helm repo server,如果要其他服务器访问,改为本地IP
+nohup helm serve --address 127.0.0.1:8879 --repo-path /opt/helm-repo &
+# 3.更改helm 配置文件
+将/etc/ansible/role/helm/default/main.yml中repo的地址改为 http://127.0.0.1:8879
+cat </etc/ansible/role/helm/default/main.yml
+helm_namespace: kube-system
+helm_cert_cn: helm001
+tiller_sa: tiller
+tiller_cert_cn: tiller001
+tiller_image: jmgao1983/tiller:v2.9.1
+#repo_url: https://kubernetes-charts.storage.googleapis.com
+repo_url: http://127.0.0.1:8879
+# 如果默认官方repo 网络访问不稳定可以使用如下的阿里云镜像repo
+#repo_url: https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
+EOF
+# 4.运行安全helm命令
+ansible-playbook /etc/ansible/role/helm/helm.yml
+```
+## 使用helm安装应用到k8s上
+
+请阅读本项目文档[helm安装prometheus监控](prometheus.md)
diff --git a/docs/guide/hpa.md b/docs/guide/hpa.md
new file mode 100644
index 0000000..cebb7f1
--- /dev/null
+++ b/docs/guide/hpa.md
@@ -0,0 +1,56 @@
+## Horizontal Pod Autoscaling
+
+自动水平伸缩,是指运行在k8s上的应用负载(POD),可以根据资源使用率进行自动扩容、缩容;我们知道应用的资源使用率通常都有高峰和低谷,所以k8s的`HPA`特性应运而生;它也是最能体现区别于传统运维的优势之一,不仅能够弹性伸缩,而且完全自动化!
+
+根据 CPU 使用率或自定义 metrics 自动扩展 Pod 数量(支持 replication controller、deployment);k8s1.6版本之前是通过kubelet来获取监控指标,1.6版本之后是通过api server、heapster或者kube-aggregator来获取监控指标。
+
+### Metrics支持
+
+根据不同版本的API中,HPA autoscale时靠以下指标来判断资源使用率:
+- autoscaling/v1: CPU
+- autoscaling/v2alpha1
+ - 内存
+ - 自定义metrics
+ - 多metrics组合: 根据每个metric的值计算出scale的值,并将最大的那个值作为扩容的最终结果
+
+### 基础示例
+
+本实验环境基于k8s 1.8 和 1.9,仅使用`autoscaling/v1` 版本API,**注意确保**`k8s` 集群插件`kubedns` 和 `heapster` 工作正常。
+
+``` bash
+# 创建deploy和service
+$ kubectl run php-apache --image=pilchard/hpa-example --requests=cpu=200m --expose --port=80
+
+# 创建autoscaler
+$ kubectl autoscale deploy php-apache --cpu-percent=50 --min=1 --max=10
+
+# 等待3~5分钟查看hpa状态
+$ kubectl get hpa php-apache
+NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
+php-apache Deployment/php-apache 0% / 50% 1 10 1 3m
+
+# 增加负载
+$ kubectl run --rm -it load-generator --image=busybox /bin/sh
+Hit enter for command prompt
+$ while true; do wget -q -O- http://php-apache; done;
+
+# 等待约5分钟查看hpa显示负载增加,且副本数目增加为4
+$ kubectl get hpa php-apache
+NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
+php-apache Deployment/php-apache 430% / 50% 1 10 4 4m
+
+# 注意k8s为了避免频繁增删pod,对副本的增加速度有限制
+# 实验过程可以看到副本数目从1到4到8到10,大概都需要4~5分钟的缓冲期
+$ kubectl get hpa php-apache
+NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
+php-apache Deployment/php-apache 86% / 50% 1 10 8 9m
+$ kubectl get hpa php-apache
+NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
+php-apache Deployment/php-apache 52% / 50% 1 10 10 12m
+
+# 清除负载,CTRL+C 结束上述循环程序,稍后副本数目变回1
+$ kubectl get hpa php-apache
+NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
+php-apache Deployment/php-apache 0% / 50% 1 10 1 17m
+```
+
diff --git a/docs/guide/index.md b/docs/guide/index.md
new file mode 100644
index 0000000..22a6519
--- /dev/null
+++ b/docs/guide/index.md
@@ -0,0 +1,22 @@
+## 使用指南
+
+### 附加组件安装
+
+- 安装 [kubedns](kubedns.md)
+- 安装 [dashboard](dashboard.md)
+- 安装 [metrics-server](metrics-server.md)
+- 安装 [prometheus](prometheus.md)
+- 安装 [heapster](heapster.md) DEPRECATED WARNNING
+- 安装 [ingress](ingress.md)
+- 安装 [helm](helm.md)
+- 安装 [efk](efk.md)
+- 安装 [harbor](harbor.md)
+- 安装 [metallb](metallb.md)
+
+### 基础特性演示
+
+- 自动水平伸缩 [Horizontal Pod Autoscaling](hpa.md)
+- 网络安全策略 [Network Policy](networkpolicy.md)
+- 滚动更新 [rollingupdate](rollingupdateWithZeroDowntime.md)
+
+
diff --git a/docs/guide/ingress-tls.md b/docs/guide/ingress-tls.md
new file mode 100644
index 0000000..5e44173
--- /dev/null
+++ b/docs/guide/ingress-tls.md
@@ -0,0 +1,106 @@
+# 使用 traefik 配置 https ingress
+
+本文档基于 traefik 配置 https ingress 规则,请先阅读[配置基本 ingress](ingress.md)。与基本 ingress-controller 相比,需要额外配置 https tls 证书,主要步骤如下:
+
+## 1.准备 tls 证书
+
+可以使用Let's Encrypt签发的免费证书,这里为了测试方便使用自签证书 (tls.key/tls.crt),注意CN 配置为 ingress 的域名:
+
+``` bash
+$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=hello.test.com"
+```
+
+## 2.在 kube-system 命名空间创建 secret: traefik-cert,以便后面 traefik-controller 挂载该证书
+
+``` bash
+$ kubectl -n kube-system create secret tls traefik-cert --key=tls.key --cert=tls.crt
+```
+
+## 3.创建 traefik-controller,增加 traefik.toml 配置文件及https 端口暴露等,详见该 yaml 文件
+
+``` bash
+$ kubectl apply -f /etc/ansible/manifests/ingress/traefik/tls/traefik-controller.yaml
+```
+
+## 4.创建 https ingress 例子
+
+``` bash
+# 创建示例应用
+$ kubectl run test-hello --image=nginx --port=80 --expose
+# hello-tls-ingress 示例
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: hello-tls-ingress
+ annotations:
+ kubernetes.io/ingress.class: traefik
+spec:
+ rules:
+ - host: hello.test.com
+ http:
+ paths:
+ - backend:
+ serviceName: test-hello
+ servicePort: 80
+ tls:
+ - secretName: traefik-cert
+# 创建https ingress
+$ kubectl apply -f /etc/ansible/manifests/ingress/traefik/tls/hello-tls.ing.yaml
+# 注意根据hello示例,需要在default命名空间创建对应的secret: traefik-cert
+$ kubectl create secret tls traefik-cert --key=tls.key --cert=tls.crt
+```
+
+## 5.验证 https 访问
+
+验证 traefik-ingress svc
+
+``` bash
+$ kubectl get svc -n kube-system traefik-ingress-service
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+traefik-ingress-service NodePort 10.68.250.253 80:23456/TCP,443:23457/TCP,8080:35941/TCP 66m
+```
+
+可以看到项目默认使用nodePort 23456暴露traefik 80端口,nodePort 23457暴露 traefik 443端口,因此在客户端 hosts 增加记录 `$Node_IP hello.test.com`之后,可以在浏览器验证访问如下:
+
+``` bash
+https://hello.test.com:23457
+```
+
+如果你已经配置了[转发 ingress nodePort](../op/loadballance_ingress_nodeport.md),那么增加对应 hosts记录后,可以验证访问 `https://hello.test.com`
+
+## 配置 dashboard ingress
+
+前提1:k8s 集群的dashboard 已安装
+
+```
+$ kubectl get svc -n kube-system | grep dashboard
+kubernetes-dashboard NodePort 10.68.211.168 443:39308/TCP 3d11h
+```
+前提2:`/etc/ansible/manifests/ingress/traefik/tls/traefik-controller.yaml`的配置文件`traefik.toml`开启了`insecureSkipVerify = true`
+
+配置 dashboard ingress:`kubectl apply -f /etc/ansible/manifests/ingress/traefik/tls/k8s-dashboard.ing.yaml` 内容如下:
+
+```
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: kubernetes-dashboard
+ namespace: kube-system
+ annotations:
+ traefik.ingress.kubernetes.io/redirect-entry-point: https
+spec:
+ rules:
+ - host: dashboard.test.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: kubernetes-dashboard
+ servicePort: 443
+```
+- 注意annotations 配置了 http 跳转 https 功能
+- 注意后端服务是443端口
+
+## 参考
+
+- [Add a TLS Certificate to the Ingress](https://docs.traefik.io/user-guide/kubernetes/#add-a-tls-certificate-to-the-ingress)
diff --git a/docs/guide/ingress.md b/docs/guide/ingress.md
new file mode 100644
index 0000000..8bd5c0f
--- /dev/null
+++ b/docs/guide/ingress.md
@@ -0,0 +1,144 @@
+## Ingress简介
+
+ingress就是从外部访问k8s集群的入口,将用户的URL请求转发到不同的service上。ingress相当于nginx反向代理服务器,它包括的规则定义就是URL的路由信息;它的实现需要部署`Ingress controller`(比如 [traefik](https://github.com/containous/traefik) [ingress-nginx](https://github.com/kubernetes/ingress-nginx) 等),`Ingress controller`通过apiserver监听ingress和service的变化,并根据规则配置负载均衡并提供访问入口,达到服务发现的作用。
+
+- 未配置ingress:
+
+集群外部 -> NodePort -> K8S Service
+
+- 配置ingress:
+
+集群外部 -> Ingress -> K8S Service
+
+- **注意:ingress 本身也需要部署`Ingress controller`时使用以下几种方式让外部访问**
+ - 使用`NodePort`方式
+ - 使用`hostPort`方式
+ - 使用LoadBalancer地址方式
+
+- 以下讲解基于`Traefik`,如果想要了解`ingress-nginx`的原理与实践,推荐阅读博客[烂泥行天下](https://www.ilanni.com/?p=14501)的相关文章
+
+### 部署 Traefik
+
+Traefik 提供了一个简单好用 `Ingress controller`,下文侧重讲解 ingress部署和测试例子。请查看yaml配置 [traefik-ingress.yaml](../../manifests/ingress/traefik/traefik-ingress.yaml),参考[traefik 官方k8s例子](https://github.com/containous/traefik/tree/master/examples/k8s)
+
+#### 安装 traefik ingress-controller
+
+``` bash
+kubectl create -f /etc/ansible/manifests/ingress/traefik/traefik-ingress.yaml
+```
++ 注意需要配置 `RBAC`授权
++ 注意`trafik pod`中 `80`端口为 traefik ingress-controller的服务端口,`8080`端口为 traefik 的管理WEB界面;为后续配置方便指定`80` 端口暴露`NodePort`端口为 `23456`(对应于在hosts配置中`NODE_PORT_RANGE`范围内可用端口)
+
+#### 验证 traefik ingress-controller
+
+``` bash
+# kubectl get deploy -n kube-system traefik-ingress-controller
+NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
+traefik-ingress-controller 1 1 1 1 4m
+
+# kubectl get svc -n kube-system traefik-ingress-service
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+traefik-ingress-service NodePort 10.68.69.170 80:23456/TCP,8080:34815/TCP 4m
+```
++ 可以看到`traefik-ingress-service` 服务端口`80`暴露的nodePort确实为`23456`
+
+#### 测试 ingress
+
++ 首先创建测试用K8S应用,并且该应用服务不用nodePort暴露,而是用ingress方式让外部访问
+
+``` bash
+kubectl run test-hello --image=nginx --expose --port=80
+##
+# kubectl get deploy test-hello
+NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
+test-hello 1 1 1 1 56s
+# kubectl get svc test-hello
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+test-hello ClusterIP 10.68.124.115 80/TCP 1m
+```
++ 然后为这个应用创建 ingress,`kubectl create -f /etc/ansible/manifests/ingress/test-hello.ing.yaml`
+
+``` bash
+# test-hello.ing.yaml内容
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: test-hello
+spec:
+ rules:
+ - host: hello.test.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: test-hello
+ servicePort: 80
+```
++ 集群内部尝试访问: `curl -H Host:hello.test.com 10.68.69.170(traefik-ingress-service的服务地址)` 能够看到欢迎页面 `Welcome to nginx!`;
++ 在集群外部尝试访问(假定集群一个NodeIP为 192.168.1.1): `curl -H Host:hello.test.com 192.168.1.1:23456`,也能够看到欢迎页面 `Welcome to nginx!`,说明ingress测试成功
+
+#### 为 traefik WEB 管理页面创建 ingress 规则
+
+`kubectl create -f /etc/ansible/manifests/ingress/traefik/traefik-ui.ing.yaml`
+
+``` bash
+# traefik-ui.ing.yaml内容
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: traefik-web-ui
+ namespace: kube-system
+spec:
+ rules:
+ - host: traefik-ui.test.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: traefik-ingress-service
+ servicePort: 8080
+```
+
++ 在集群外部可以使用 `curl -H Host:traefik-ui.test.com 192.168.1.1:23456` 尝试访问WEB管理页面,返回 `Found .`说明 traefik-ui的ingress配置生效了。
+
++ 在客户端主机也可以通过修改本机 `hosts` 文件,如上例子,增加两条记录:
+
+``` text
+192.168.1.1 hello.test.com
+192.168.1.1 traefik-ui.test.com
+```
+打开浏览器输入域名 `http://hello.test.com:23456` 和 `http://traefik-ui.test.com:23456` 就可以访问k8s的应用服务了。
+
+### 可选1: 使用`LoadBalancer`服务类型来暴露ingress,自有环境(非公有云)可以参考[metallb文档](metallb.md)
+
+``` bash
+# 修改traefik-ingress 使用 LoadBalancer服务
+$ sed -i 's/NodePort$/LoadBalancer/g' /etc/ansible/manifests/ingress/traefik/traefik-ingress.yaml
+# 创建traefik-ingress
+$ kubectl apply -f /etc/ansible/manifests/ingress/traefik/traefik-ingress.yaml
+# 验证
+$ kubectl get svc --all-namespaces |grep traefik
+kube-system traefik-ingress-service LoadBalancer 10.68.163.243 192.168.1.241 80:23456/TCP,8080:37088/TCP 1m
+```
+这时可以修改客户端本机 `hosts`文件:(如上例192.168.1.241)
+
+``` text
+192.168.1.241 hello.test.com
+192.168.1.241 traefik-ui.test.com
+```
+打开浏览器输入域名 `http://hello.test.com` 和 `http://traefik-ui.test.com`可以正常访问。
+
+### 可选2: 部署`ingress-service`的负载均衡
+
+- 利用 nginx/haproxy 等集群,可以做代理转发以去掉 `23456`这个端口。如果你的集群根据本项目部署了高可用方案,那么可以利用`LB` 节点haproxy 来做,当然如果生产环境K8S应用已经部署非常多,建议还是使用独立的 `nginx/haproxy`集群。
+
+具体参考[配置转发 ingress nodePort](../op/loadballance_ingress_nodeport.md),如上配置访问集群`MASTER_IP`的`80`端口时,由haproxy代理转发到实际的node节点暴露的nodePort端口上了。这时可以修改客户端本机 `hosts`文件如下:(假定 MASTER_IP=192.168.1.10)
+
+``` text
+192.168.1.10 hello.test.com
+192.168.1.10 traefik-ui.test.com
+```
+打开浏览器输入域名 `http://hello.test.com` 和 `http://traefik-ui.test.com`可以正常访问。
+
+## 下一步[配置https ingress](ingress-tls.md)
diff --git a/docs/guide/ipvs.md b/docs/guide/ipvs.md
new file mode 100644
index 0000000..882a24d
--- /dev/null
+++ b/docs/guide/ipvs.md
@@ -0,0 +1,35 @@
+# IPVS 服务负载均衡
+
+kube-proxy 组件监听 API server 中 service 和 endpoint 的变化情况,从而为 k8s 集群内部的 service 提供动态负载均衡。在v1.10之前主要通过 iptables来实现,是稳定、推荐的方式,但是当服务多的时候会产生太多的 iptables 规则,大规模情况下有明显的性能问题;在v1.11 GA的 ipvs高性能负载模式,采用增量式更新,并可以保证 service 更新期间连接的保持。
+
+- NOTE: k8s v1.11.0 CentOS7下使用ipvs模式会有问题(见 kubernetes/kubernetes#65461),测试 k8s v1.10.2 CentOS7 可以。
+
+## 启用 ipvs
+
+k8s v1.11 版本启用 ipvs 十分方便,只要在 kube-proxy 启动参数(或者配置文件中)中增加 `--proxy-mode=ipvs`:
+
+``` bash
+[Unit]
+Description=Kubernetes Kube-Proxy Server
+After=network.target
+
+[Service]
+WorkingDirectory=/var/lib/kube-proxy
+ExecStart={{ bin_dir }}/kube-proxy \
+ --bind-address={{ NODE_IP }} \
+ --hostname-override={{ NODE_IP }} \
+ --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig \
+ --logtostderr=true \
+ --proxy-mode=ipvs
+Restart=on-failure
+RestartSec=5
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
+```
+
+目前 kubeasz 0.2.2 支持两种方式集成使用 ipvs:一种是使用kube-proxy 自带的ipvs 实现;另一种是使用 kube-router网络插件带的ipvs service-proxy;
+
+- v1.11, v1.10 版本启用 kube-proxy的 ipvs 模式:只需要在 `roles/kube-node/defaults/main.yml` 配置 `PROXY_MODE: "ipvs"`
+- 启用 kube-router 的 ipvs service-proxy:需在 ansible hosts 配置选择 `CLUSTER_NETWORK="kube-router"`;另外在 `roles/kube-router/defaults/main.yml` 配置 `SERVICE_PROXY: "true"`
diff --git a/docs/guide/istio.md b/docs/guide/istio.md
new file mode 100644
index 0000000..f97c8f0
--- /dev/null
+++ b/docs/guide/istio.md
@@ -0,0 +1,173 @@
+---
+title: "Istio 1.0.3 安装 "
+date: 2018-11-12T13:44:34+08:00
+draft: false
+---
+
+#### Service Mesh(服务网格)
+
+---
+Kubernetes 已经给我们带来了诸多的好处。但是仍有些需求比如 A/B 测试、金丝雀发布、限流、访问控制,端到端认证等需要运维人员进一步去解决。
+
+Istio 是完全开源的服务网格,提供了一套完整的解决方案,可以透明地分层到现有的分布式应用程序上。对开发人员几乎无感的同时获得超能力。
+
+如果想要现有的服务支持 Istio,只需要在当前的环境中部署一个特殊的 sidecar 代理,即可。
+
+##### 前提
+
+----
+
+- 安装 Kubernetes 集群 1.9+
+- [安装 Helm](./helm.md)
+
+##### 准备
+
+----
+
+进入 [Istio release](https://github.com/istio/istio/releases) 页面下载最新版安装包(1.0.3)并解压到当前目录,
+
+```
+curl -L https://git.io/getLatestIstio | sh -
+
+ll istio-1.0.3/
+total 28
+drwxr-xr-x 2 root root 22 10月 26 07:36 bin
+drwxr-xr-x 6 root root 79 10月 26 07:36 install
+-rw-r--r-- 1 root root 648 10月 26 07:36 istio.VERSION
+-rw-r--r-- 1 root root 11343 10月 26 07:36 LICENSE
+-rw-r--r-- 1 root root 5817 10月 26 07:36 README.md
+drwxr-xr-x 12 root root 212 10月 26 07:36 samples
+drwxr-xr-x 8 root root 4096 10月 26 07:36 tools
+```
+- install Kubernetes 安装所需的 .yaml 文件
+- samples Task中的示例应用
+- bin/istioctl 客户端工具
+- istio.VERSION 配置文件
+
+#### 安装
+
+---
+
+##### 安装 istio
+注意事项
+
+Istio 默认使用‘负载均衡器’服务对象类型。对于裸机安装没有负载均衡器的情况下,安装需指定‘NodePort’类型。
+
+```
+helm install --name istio install/kubernetes/helm/istio --namespace istio-system --set gateways.istio-ingressgateway.type=NodePort --set gateways.istio-egressgateway.type=NodePort
+```
+
+##### 验证
+```
+kubectl get pod -n istio-system
+NAME READY STATUS RESTARTS AGE
+istio-citadel-6955bc9cb7-qh846 1/1 Running 0 3d22h
+istio-egressgateway-7dc5cbbc56-k4cgh 1/1 Running 0 3d22h
+istio-galley-545b6b8f5b-k7ssx 1/1 Running 0 3d22h
+istio-ingressgateway-7958d776b5-ptdsc 1/1 Running 0 3d22h
+istio-pilot-56bfdbffff-mtcn6 2/2 Running 0 3d22h
+istio-policy-5c689f446f-6bzlq 2/2 Running 0 3d15h
+istio-policy-5c689f446f-dvmfq 2/2 Running 0 3d22h
+istio-policy-5c689f446f-f2kl8 2/2 Running 0 3d3h
+istio-policy-5c689f446f-nfv2l 2/2 Running 0 3d1h
+istio-policy-5c689f446f-qdtql 2/2 Running 0 3d2h
+istio-sidecar-injector-99b476b7b-dt24k 1/1 Running 0 3d22h
+istio-telemetry-55d68b5dfb-52ftl 2/2 Running 0 3d22h
+istio-telemetry-55d68b5dfb-dvdvz 2/2 Running 0 3d22h
+istio-telemetry-55d68b5dfb-ln2sr 2/2 Running 0 3d
+istio-telemetry-55d68b5dfb-m2mb8 2/2 Running 0 3d
+istio-telemetry-55d68b5dfb-sjgq8 2/2 Running 0 3d
+prometheus-65d6f6b6c-dsv26 1/1 Running 0 3d22h
+
+```
+```
+kubectl get svc -n istio-system
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+istio-citadel ClusterIP 10.68.7.100 8060/TCP,9093/TCP 3d22h
+istio-egressgateway NodePort 10.68.67.237 80:30060/TCP,443:38194/TCP 3d22h
+istio-galley ClusterIP 10.68.12.54 443/TCP,9093/TCP 3d22h
+istio-ingressgateway NodePort 10.68.87.79 80:31380/TCP,443:31390/TCP,31400:31400/TCP,15011:31812/TCP,8060:30957/TCP,853:23011/TCP,15030:22292/TCP,15031:23663/TCP 3d22h
+istio-pilot ClusterIP 10.68.84.101 15010/TCP,15011/TCP,8080/TCP,9093/TCP 3d22h
+istio-policy ClusterIP 10.68.94.206 9091/TCP,15004/TCP,9093/TCP 3d22h
+istio-sidecar-injector ClusterIP 10.68.191.221 443/TCP 3d22h
+istio-telemetry ClusterIP 10.68.199.8 9091/TCP,15004/TCP,9093/TCP,42422/TCP 3d22h
+prometheus ClusterIP 10.68.91.13 9090/TCP
+```
+
+##### Sidecar 的自动注入
+
+注意事项
+
+需要在kube-apiserver 启动 admission-control 参数中加入 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook并确保正确的顺序,如果是多master安装,确保每个kube-apiserver都要进行修改。
+
+```
+/bin/kube-apiserver --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook
+```
+
+重启 kube-apiserver 即可
+
+##### 部署应用验证
+
+istio 的samples目录中有很多示例。我们现在使用samples/sleep/sleep.yaml 来验证刚刚开启的Sidecar自动注入功能。
+
+进入目录 istio-1.0.3/ 部署一个新的应用
+
+```
+cd istio-1.0.3/
+kubectl apply -f samples/sleep/sleep.yaml
+
+kubectl get pod
+NAME READY STATUS RESTARTS AGE
+sleep-7549f66447-wv8cl 1/1 Running 0 1m
+```
+
+一切都是熟悉的味道。下面给 default 命名空间设置标签:istio-injection=enabled,这样就会在pod 创建时触发 Sidecar 的注入过程。从此default 名称空间拥有了超能力.
+
+```
+kubectl label namespace default istio-injection=enabled
+kubectl get namespace -L istio-injection
+NAME STATUS AGE ISTIO-INJECTION
+default Active 1h enabled
+istio-system Active 3d22h
+kube-public Active 4d2h
+kube-system Active 4d2h
+```
+接下来删除上面创建的pod,观察下有什么变化。
+
+```
+kubectl delete pod sleep-7549f66447-wv8cl
+pod "sleep-7549f66447-wv8cl" deleted
+
+kubectl get pod
+NAME READY STATUS RESTARTS AGE
+sleep-7549f66447-x4td6 2/2 Running 0 37s
+```
+刚刚的pod里面现在已经拥有两个容器,进入pod一探究竟。
+```
+ kubectl describe pod sleep-7549f66447-x4td6
+
+ ....
+
+ Containers:
+ sleep:
+
+ ....
+
+ istio-proxy:
+
+ ....
+
+```
+多出了一个 istio-proxy 容器及其对应的存储卷
+
+
+#### 卸载istio
+
+---
+
+```
+helm delete --purge istio
+
+```
+
+
diff --git a/docs/guide/jenkins.md b/docs/guide/jenkins.md
new file mode 100644
index 0000000..800395b
--- /dev/null
+++ b/docs/guide/jenkins.md
@@ -0,0 +1,180 @@
+# Jenkins CI/CD
+
+## 前言
+本文档介绍如何快速通过K8s集群实现Jenkins 动态Slave CI/CD流程。
+
+## 开始之前
+在开始之前需要准备以下环境:
+- k8s dns组件
+参考文档:[kubedns](kubedns.md)
+- helm
+为了简化部署,通过helm来安装Jenkins,可参考文档:[helm](helm.md)
+- 持久化存储
+这里使用**NFS**演示,参考文档:[cluster-storage](../setup/08-cluster-storage.md)。
+如果k8s集群是部署在公有云,也可使用厂商的NAS等存储方案,项目中已集成支持阿里云NAS,其他的方案参考相关厂商文档
+
+- Ingress Controller(nginx-ingress/traefik)
+默认是通过Ingress访问Jenkins,因此需要安装一种`Ingress Controller`。参考文档:[ingress](ingress.md)
+- Gitlab 代码管理仓库
+用于提交代码后自动触发CI, 目前项目中还没有相关内容,可[参考官网](https://about.gitlab.com/installation/)进行安装。
+
+## 安装Jenkins
+执行以下命令快速安装:
+```
+helm install manifests/jenkins/ --name jenkins
+```
+如果通过/etc/ansible/roles/helm/helm.yml安装的helm,安装过程会出现如下错误
+
+``` bash
+E0703 08:40:22.376225 19888 portforward.go:331] an error occurred forwarding 41655 -> 44134: error forwarding port 44134 to pod 5098414beaaa07140a4ba3240690b1ce989ece01e5db33db65eec83bd64bdedf, uid : exit status 1: 2018/07/03 08:40:22 socat[19991] E write(5, 0x1aec120, 3424): Connection reset by peer
+Error: transport is closing
+```
+请执行以下命令快速安装进行修复:
+```
+helm install --tls manifests/jenkins/ --name jenkins
+```
+
+由于初始化过程中,默认安装指定的插件,所以启动较慢,大概5-10分钟左右就可以启动完成了。
+
+部分默认配置说明:
+**注**:以下配置都定义在`manifests/jenkins/values.yaml`文件中。
+
+
+ 字段
+ 说明
+ 默认值
+
+
+ InstallPlugins
+ 初始化安装的插件
+
+
+ kubernetes:1.6.3
+ workflow-aggregator:2.5
+ workflow-job:2.21
+ credentials-binding:1.16
+ git:3.9.0
+ gitlab:1.5.6
+
+
+
+
+ HostName
+ Ingress访问入口
+ jenkins.local.com
+
+
+ AdminPassword
+ admin登录密码
+ admin
+
+
+ UpdateCenter
+ 插件下载镜像地址
+ https://mirrors.tuna.tsinghua.edu.cn/jenkins
+
+
+ StorageClass
+ 持久化存储SC
+ nfs-dynamic-class
+
+
+
+
+## 配置Kubernetes plugin
+登录Jenkins,点击左边导航`系统管理`——>`系统设置`,拖动到最下面可以看到`云——>Kubernetes`配置,默认配置有以下字段:
+
+- Name:配置名称,后面运行测试的时候会用到,用于区别多个Kubernetes配置,默认为:kubernetes
+- Kubernetes URL:集群访问url,可通过`kubectl cluster-info`查看,如果集群有部署**DNS**插件, 也可以直接填服务名称(自动解析),默认使用服务名称:https://kubernetes
+- Jenkins URL:Jenkins访问地址,默认使用服务名称+端口号
+
+在Jenkins初始化时,默认都已经配置好了,可以直接新建项目测试了。
+
+## 简单测试
+点击左边:新建任务——>流水线(Pipeline)
+任务名称可以随便起,这里为:k8s-test
+配置——>流水线,选择`Pipeline script`
+以下为测试脚本内容:
+```
+podTemplate(label: 'jenkins-slave', cloud: 'kubernetes')
+{
+ node ('jenkins-slave') {
+ stage('test') {
+ echo "hello, world"
+ sleep 60
+ }
+ }
+}
+```
+
+- cloud:插件配置中的Name
+- label:插件配置中的Images——>Kubernetes Pod Tempalte——>Labels
+- node:与label一致即可
+
+保存配置,点击立即构建,查看控制台输出,出现以下内容就表示运行成功了:
+```
+Agent default-lsths is provisioned from template Kubernetes Pod Template
+Agent specification [Kubernetes Pod Template] (jenkins-slave):
+* [jnlp] jenkins/jnlp-slave:alpine(resourceRequestCpu: 200m, resourceRequestMemory: 256Mi, resourceLimitCpu: 200m, resourceLimitMemory: 256Mi)
+
+Running on default-lsths in /home/jenkins/workspace/k8s-test
+[Pipeline] {
+[Pipeline] stage
+[Pipeline] { (test)
+[Pipeline] echo
+hello, world
+[Pipeline] sleep
+Sleeping for 1 min 0 sec
+[Pipeline] }
+[Pipeline] // stage
+[Pipeline] }
+[Pipeline] // node
+[Pipeline] }
+[Pipeline] // podTemplate
+[Pipeline] End of Pipeline
+Finished: SUCCESS
+```
+
+
+## 配置自动触发CI
+
+- 配置Gitlab项目
+在`Gitlab`中创建一个测试项目,将上面测试的脚本内容写入到一个`Jenkinsfile`文件中,然后上传到该测试项目根路径下。
+
+- 配置Jenkins项目
+点击项目`配置`——>`构建触发器`——>勾选`Build when a change is pushed to GitLab. GitLab webhook URL:http://jenkins.local.com/project/k8s-test`——>保存配置
+
+- 配置Webhook
+进入Gitlab测试项目的`Settings——>Integrations`,一般只需要填写`URL`即可,其他的可根据需求环境配置
+默认Jenkins配置不允许匿名用户触发构建,因此还需要添加用户和token。
+URL的格式为:
+`http://[UserID]:[API Token]@jenkins.local.com/project/[ProjectName]`
+
+Jenkins 用户ID Token查看:
+点击右上角的`用户名——>设置——>API Token(点击Show API Token...)`
+
+最终Webhook中的URL类似:
+http://admin:a910b1492e39e9dd1ea48ea7f7638aaf@jenkins.local.com/project/k8s-test
+
+后面只需要我们一提交代码到Git仓库,就会自动触发Jenkins进行构建了。
+
+## 项目应用
+这里我们以一个简单的Java项目为例,实战演示如何进行CI/CD。
+基本环境配置上面已经说过了,这里就不多介绍。
+示例项目:https://github.com/lusyoe/springboot-k8s-example
+
+结构说明:
+- 镜像构建文件:`Dockerfile`
+- k8s应用配置:`k8s-example.yaml`
+- 项目源码:`src`
+- Jenkins构建文件:`jenkins/Jenkinsfile`
+
+构建流程说明:
+- 通过Jenkins kubernetes插件,定义构建过程中所需的3个docker容器:maven、docker、kubectl (这3个容器都在一个pod中)
+- 挂载docker.sock和kubeconfig文件
+- 首先使用`maven`容器,检出代码,执行项目构建
+- 使用`docker`容器,构建镜像,推送到镜像参考
+- 使用`kubectl`容器,部署`k8s-example`应用(这里后面也可以使用helm)
+
+访问:
+项目通过Ingress访问`k8s-example.com`,出现`hello, world`,就表示服务部署成功了。
diff --git a/docs/guide/kernel_upgrade.md b/docs/guide/kernel_upgrade.md
new file mode 100644
index 0000000..6409530
--- /dev/null
+++ b/docs/guide/kernel_upgrade.md
@@ -0,0 +1,45 @@
+# Linux Kernel 升级
+
+k8s,docker,cilium等很多功能、特性需要较新的linux内核支持,所以有必要在集群部署前对内核进行升级;CentOS7 和 Ubuntu16.04可以很方便的完成内核升级。
+
+## CentOS7
+
+``` bash
+# 载入公钥
+rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
+# 安装ELRepo
+rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
+# 载入elrepo-kernel元数据
+yum --disablerepo=\* --enablerepo=elrepo-kernel repolist
+# 查看可用的rpm包
+yum --disablerepo=\* --enablerepo=elrepo-kernel list kernel*
+# 安装最新版本的kernel
+yum --disablerepo=\* --enablerepo=elrepo-kernel install -y kernel-ml.x86_64
+# 删除旧版本工具包
+yum remove kernel-tools-libs.x86_64 kernel-tools.x86_64 -y
+# 安装新版本工具包
+yum --disablerepo=\* --enablerepo=elrepo-kernel install -y kernel-ml-tools.x86_64
+
+#查看默认启动顺序
+awk -F\' '$1=="menuentry " {print $2}' /etc/grub2.cfg
+CentOS Linux (4.4.4-1.el7.elrepo.x86_64) 7 (Core)
+CentOS Linux (3.10.0-327.10.1.el7.x86_64) 7 (Core)
+CentOS Linux (0-rescue-c52097a1078c403da03b8eddeac5080b) 7 (Core)
+#默认启动的顺序是从0开始,新内核是从头插入(目前位置在0,而4.4.4的是在1),所以需要选择0。
+grub2-set-default 0
+#重启并检查
+reboot
+```
+
+## Ubuntu16.04
+
+``` bash
+打开 http://kernel.ubuntu.com/~kernel-ppa/mainline/ 并选择列表中选择你需要的版本(以4.16.3为例)。
+接下来,根据你的系统架构下载 如下.deb 文件:
+Build for amd64 succeeded (see BUILD.LOG.amd64):
+ linux-headers-4.16.3-041603_4.16.3-041603.201804190730_all.deb
+ linux-headers-4.16.3-041603-generic_4.16.3-041603.201804190730_amd64.deb
+ linux-image-4.16.3-041603-generic_4.16.3-041603.201804190730_amd64.deb
+#安装后重启即可
+$ sudo dpkg -i *.deb
+```
diff --git a/docs/guide/kubedns.md b/docs/guide/kubedns.md
new file mode 100644
index 0000000..67c74ee
--- /dev/null
+++ b/docs/guide/kubedns.md
@@ -0,0 +1,84 @@
+## 部署集群 DNS
+
+DNS 是 k8s 集群首先需要部署的,集群中的其他 pods 使用它提供域名解析服务;主要可以解析 `集群服务名 SVC` 和 `Pod hostname`;目前 k8s v1.9+ 版本可以有两个选择:`kube-dns` 和 `coredns`,可以选择其中一个部署安装。
+
+### 部署 dns
+
+配置文件参考 `https://github.com/kubernetes/kubernetes` 项目目录 `kubernetes/cluster/addons/dns`
+
++ 安装
+
+``` bash
+# 安装 kube-dns
+$ kubectl create -f /etc/ansible/manifests/kubedns
+
+# 或者选择安装 coredns
+$ kubectl create -f /etc/ansible/manifests/coredns
+```
+
++ 集群 pod默认继承 node的dns 解析,修改 kubelet服务启动参数 --resolv-conf="",可以更改这个特性,详见 kubelet 启动参数
+
+### 验证 dns服务
+
+新建一个测试nginx服务
+
+`kubectl run nginx --image=nginx --expose --port=80`
+
+确认nginx服务
+
+``` bash
+kubectl get pod|grep nginx
+nginx-7cbc4b4d9c-fl46v 1/1 Running 0 1m
+kubectl get svc|grep nginx
+nginx ClusterIP 10.68.33.167 80/TCP 1m
+```
+
+测试pod alpine
+
+``` bash
+kubectl run test --rm -it --image=alpine /bin/sh
+If you don't see a command prompt, try pressing enter.
+/ # cat /etc/resolv.conf
+nameserver 10.68.0.2
+search default.svc.cluster.local. svc.cluster.local. cluster.local.
+options ndots:5
+# 测试集群内部服务解析
+/ # nslookup nginx.default.svc.cluster.local
+Server: 10.68.0.2
+Address 1: 10.68.0.2 kube-dns.kube-system.svc.cluster.local
+
+Name: nginx
+Address 1: 10.68.33.167 nginx.default.svc.cluster.local
+/ # nslookup kubernetes.default.svc.cluster.local
+Server: 10.68.0.2
+Address 1: 10.68.0.2 kube-dns.kube-system.svc.cluster.local
+
+Name: kubernetes
+Address 1: 10.68.0.1 kubernetes.default.svc.cluster.local
+# 测试外部域名的解析,默认集成node的dns解析
+/ # nslookup www.baidu.com
+Server: 10.68.0.2
+Address 1: 10.68.0.2 kube-dns.kube-system.svc.cluster.local
+
+Name: www.baidu.com
+Address 1: 180.97.33.108
+Address 2: 180.97.33.107
+/ #
+```
+
+- Note1: 如果你使用`calico`网络组件,通过命令`ansible-playbook 90.setup.yml`安装完集群后,直接安装dns组件,可能会出现如下BUG,分析是因为calico分配pod地址时候会从网段的第一个地址(网络地址)开始,详见提交的 [ISSUE #1710](https://github.com/projectcalico/calico/issues/1710),临时解决办法为手动删除POD,重新创建后获取后面的IP地址
+
+```
+# BUG出现现象
+$ kubectl get pod --all-namespaces -o wide
+NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE
+default busy-5cc98488d4-s894w 1/1 Running 0 28m 172.20.24.193 192.168.97.24
+kube-system calico-kube-controllers-6597d9c664-nq9hn 1/1 Running 0 1h 192.168.97.24 192.168.97.24
+kube-system calico-node-f8gnf 2/2 Running 0 1h 192.168.97.24 192.168.97.24
+kube-system kube-dns-69bf9d5cc9-c68mw 0/3 CrashLoopBackOff 27 31m 172.20.24.192 192.168.97.24
+
+# 解决办法,删除pod,自动重建
+$ kubectl delete pod -n kube-system kube-dns-69bf9d5cc9-c68mw
+```
+
+- Note2: 使用``` kubectl run test -it --rm --image=busybox /bin/sh``` 进行解析测试可能会失败, busybox内的nslookup程序有bug, 详见 https://github.com/kubernetes/dns/issues/109
diff --git a/docs/guide/metallb.md b/docs/guide/metallb.md
new file mode 100644
index 0000000..989e116
--- /dev/null
+++ b/docs/guide/metallb.md
@@ -0,0 +1,110 @@
+# metallb 网络负载均衡
+
+`Metallb`是在自有硬件上(非公有云)实现 `Kubernetes Load-balancer`的工具,由`google`团队开源,值得推荐!项目[github主页](https://github.com/google/metallb)。
+
+## metallb 简介
+
+这里简单介绍下它的实现原理,具体可以参考[metallb官网](https://metallb.universe.tf/),文档非常简洁、清晰。目前有如下的使用限制:
+
+- `Kubernetes v1.9.0`版本以上,暂不支持`ipvs`模式
+- 支持网络组件 (flannel/weave/romana), calico 部分支持
+- `layer2`和`bgp`两种模式,其中`bgp`模式需要外部网络设备支持`bgp`协议
+
+`metallb`主要实现了两个功能:地址分配和对外宣告
+
+- 地址分配:需要向网络管理员申请一段ip地址,如果是layer2模式需要这段地址与node节点地址同个网段(同一个二层);如果是bgp模式没有这个限制。
+- 对外宣告:layer2模式使用arp协议,利用节点的mac额外宣告一个loadbalancer的ip(同mac多ip);bgp模式下节点利用bgp协议与外部网络设备建立邻居,宣告loadbalancer的地址段给外部网络。
+
+## kubeasz 集成安装metallb
+
+因bgp模式需要外部路由器的支持,这里主要选用layer2模式(如需选择bgp模式,相应修改roles/cluster-addon/templates/metallb/bgp.yaml.j2)。
+
+- 1.修改roles/cluster-addon/defaults/main.yml 配置文件相关
+
+``` bash
+# metallb 自动安装
+metallb_install: "yes"
+# 模式选择: 二层 "layer2" 或者三层 "bgp"
+metallb_protocol: "layer2"
+metallb_offline: "metallb_v0.7.3.tar"
+metallb_vip_pool: "192.168.1.240/29" # 选一段与node节点相同网段的地址
+```
+
+- 2.执行安装 `ansible-playbook 07.cluster-addon.yml`,其中controller 负责统一loadbalancer地址管理和服务监控,speaker 负责节点的loadbalancer地址的对外宣告(使用arp或者bgp网络协议),注意 **speaker是以DaemonSet 形式运行且只会调度到有node-role.kubernetes.io/metallb-speaker=true标签的节点**,所以你可以选择做speaker的节点(该节点网络性能要好),使用命令 `$ kubectl label nodes 192.168.1.43 node-role.kubernetes.io/metallb-speaker=true`
+
+- 3.验证metallb相关 pod
+
+``` bash
+$ kubectl get node
+NAME STATUS ROLES AGE VERSION
+192.168.1.41 Ready,SchedulingDisabled master 4h v1.11.3
+192.168.1.42 Ready node 4h v1.11.3
+192.168.1.43 Ready metallb-speaker,node 4h v1.11.3
+192.168.1.44 Ready metallb-speaker,node 4h v1.11.3
+$ kubectl get pod -n metallb-system
+NAME READY STATUS RESTARTS AGE
+controller-9c57dbd4-798nb 1/1 Running 0 4h
+speaker-9rjmk 1/1 Running 0 4h
+speaker-n79l4 1/1 Running 0 4h
+```
+
+- 3.创建测试应用验证 loadbalancer 地址分配
+
+``` bash
+# 创建测试应用
+$ cat > test-nginx.yaml << EOF
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: nginx3
+spec:
+ selector:
+ matchLabels:
+ app: nginx3
+ template:
+ metadata:
+ labels:
+ app: nginx3
+ spec:
+ containers:
+ - name: nginx3
+ image: nginx:1
+ ports:
+ - name: http
+ containerPort: 80
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: nginx3
+spec:
+ ports:
+ - name: http
+ port: 80
+ protocol: TCP
+ targetPort: 80
+ selector:
+ app: nginx3
+ type: LoadBalancer
+EOF
+$ kubectl apply -f test-nginx.yaml
+
+# 查看生成的loadbalancer 地址,如下验证成功
+$ kubectl get svc
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+kubernetes ClusterIP 10.68.0.1 443/TCP 5h
+nginx3 LoadBalancer 10.68.82.227 192.168.1.240 80:38702/TCP 1m
+```
+
+- 4.验证使用loadbalacer 来暴露ingress的服务地址,之前在[ingress文档](ingress.md)中我们是使用nodeport方式服务类型,现在我们可以方便的使用loadbalancer类型了,使用loadbalancer地址(192.168.1.241)方便的绑定你要的域名进行访问。
+
+``` bash
+# 修改traefik-ingress 使用 LoadBalancer服务
+$ sed -i 's/NodePort$/LoadBalancer/g' /etc/ansible/manifests/ingress/traefik/traefik-ingress.yaml
+# 创建traefik-ingress
+$ kubectl apply -f /etc/ansible/manifests/ingress/traefik/traefik-ingress.yaml
+# 验证
+$ kubectl get svc --all-namespaces |grep traefik
+kube-system traefik-ingress-service LoadBalancer 10.68.163.243 192.168.1.241 80:23456/TCP,8080:37088/TCP 1m
+```
diff --git a/docs/guide/metrics-server.md b/docs/guide/metrics-server.md
new file mode 100644
index 0000000..e964d5e
--- /dev/null
+++ b/docs/guide/metrics-server.md
@@ -0,0 +1,60 @@
+# Metrics Server
+
+从 v1.8 开始,资源使用情况的度量(如容器的 CPU 和内存使用)可以通过 Metrics API 获取;前提是集群中要部署 Metrics Server,它从Kubelet 公开的Summary API采集指标信息,关于更多的背景介绍请参考如下文档:
+- Metrics Server[设计提案](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/instrumentation/metrics-server.md)
+
+大致是说它符合k8s的监控架构设计,受heapster项目启发,并且比heapster优势在于:访问不需要apiserver的代理机制,提供认证和授权等;很多集群内组件依赖它(HPA,scheduler,kubectl top),因此它应该在集群中默认运行;部分k8s集群的安装工具已经默认集成了Metrics Server的安装,以下概述下它的安装:
+
+- 1.metric-server是扩展的apiserver,依赖于[kube-aggregator](https://github.com/kubernetes/kube-aggregator),因此需要在apiserver中开启相关参数。
+- 2.需要在集群中运行deployment处理请求
+
+从kubeasz 0.1.0 开始,metrics-server已经默认集成在集群安装脚本中,请查看`roles/cluster-addon/defaults/main.yml`中的设置
+
+## 安装
+
+默认已集成在90.setup.yml中,如果分步请执行`ansible-play /etc/ansible/07.cluster-addon.yml`
+
+- 1.设置apiserver相关[参数](../../roles/kube-master/templates/kube-apiserver.service.j2)
+``` bash
+... # 省略
+ --requestheader-client-ca-file={{ ca_dir }}/ca.pem \
+ --requestheader-allowed-names=aggregator \
+ --requestheader-extra-headers-prefix=X-Remote-Extra- \
+ --requestheader-group-headers=X-Remote-Group \
+ --requestheader-username-headers=X-Remote-User \
+ --proxy-client-cert-file={{ ca_dir }}/aggregator-proxy.pem \
+ --proxy-client-key-file={{ ca_dir }}/aggregator-proxy-key.pem \
+ --enable-aggregator-routing=true \
+```
+- 2.生成[aggregator proxy相关证书](../../roles/kube-master/tasks/main.yml)
+
+参考1:https://kubernetes.io/docs/tasks/access-kubernetes-api/configure-aggregation-layer/
+参考2:https://kubernetes.io/docs/tasks/access-kubernetes-api/setup-extension-api-server/
+
+## 验证
+
+- 查看生成的新api:v1beta1.metrics.k8s.io
+``` bash
+$ kubectl get apiservice|grep metrics
+v1beta1.metrics.k8s.io 1d
+```
+
+- 查看kubectl top命令(无需额外安装heapster)
+``` bash
+$ kubectl top node
+NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
+192.168.1.1 116m 2% 2342Mi 60%
+192.168.1.2 79m 1% 1824Mi 47%
+192.168.1.3 82m 2% 1897Mi 49%
+$ kubectl top pod --all-namespaces # 输出略
+```
+
+- 验证基于metrics-server实现的基础hpa自动缩放,请参考[hpa.md](hpa.md)
+
+## 补充
+
+目前dashboard插件如果想在界面上显示资源使用率,它还依赖于`heapster`;另外,测试发现k8s 1.8版本的`kubectl top`也依赖`heapster`,因此建议补充安装`heapster`,无需安装`influxdb`和`grafana`。
+
+``` bash
+$ kubectl apply -f /etc/ansible/manifests/heapster/heapster.yaml
+```
diff --git a/docs/guide/networkpolicy.md b/docs/guide/networkpolicy.md
new file mode 100644
index 0000000..a08635d
--- /dev/null
+++ b/docs/guide/networkpolicy.md
@@ -0,0 +1,189 @@
+## Network Policy
+
+`Network Policy`提供了基于策略的网络控制,用于隔离应用并减少攻击面。它使用标签选择器模拟传统的分段网络,并通过策略控制它们之间的流量以及来自外部的流量;目前基于`linux iptables`实现,使用类似`nf_conntrack`检查记录网络流量`session`从而决定流量是否阻断;因此它是`状态检测防火墙`。
+
+- `k8s v1.7`版本以后,Network Policy已经默认可用;`v1.6`以及之前版本需要在kube-apiserver中开启extensions/v1beta1/networkpolicies
+- 网络插件要支持 Network Policy,如 Calico、Romana、Weave Net
+
+### 简单示例
+
+实验环境:k8s v1.9, calico 2.6.5
+
+首先部署测试用nginx服务
+
+``` bash
+$ kubectl run nginx --image=nginx --replicas=3 --port=80 --expose
+# 验证测试nginx服务
+$ kubectl get pod -o wide
+NAME READY STATUS RESTARTS AGE IP NODE
+nginx-7587c6fdb6-p2fpz 1/1 Running 0 55m 172.20.125.2 10.0.96.7
+nginx-7587c6fdb6-pbw7c 1/1 Running 0 55m 172.20.124.2 10.0.96.6
+nginx-7587c6fdb6-v48db 1/1 Running 0 55m 172.20.121.195 10.0.96.4
+$ kubectl get svc nginx
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+nginx ClusterIP 10.68.7.183 80/TCP 1h
+```
+默认情况下,其他pod可以访问nginx服务
+
+``` bash
+$ kubectl run busy1 --rm -it --image=busybox /bin/sh
+If you don't see a command prompt, try pressing enter.
+/ # wget --spider --timeout=1 nginx
+Connecting to nginx (10.68.7.183:80)
+```
+创建`DefaultDeny Network Policy`后,其他Pod(包括namespace外部)不能访问nginx
+
+``` bash
+$ cat > default-deny.yaml << EOF
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ name: default-deny
+spec:
+ podSelector: {}
+ policyTypes:
+ - Ingress
+EOF
+$ kubectl create -f default-deny.yaml
+networkpolicy "default-deny" created
+$ kubectl run busy1 --rm -it --image=busybox /bin/sh
+If you don't see a command prompt, try pressing enter.
+/ # wget --spider --timeout=1 nginx
+Connecting to nginx (10.68.7.183:80)
+wget: download timed out
+```
+创建一个允许带有access=true的Pod访问nginx的网络策略
+
+``` bash
+$ cat > nginx-policy.yaml << EOF
+kind: NetworkPolicy
+apiVersion: networking.k8s.io/v1
+metadata:
+ name: access-nginx
+spec:
+ podSelector:
+ matchLabels:
+ run: nginx
+ ingress:
+ - from:
+ - podSelector:
+ matchLabels:
+ access: "true"
+EOF
+$ kubectl create -f nginx-policy.yaml
+networkpolicy "access-nginx" created
+
+# 不带access=true标签的Pod还是无法访问nginx服务
+$ kubectl run busy1 --rm -it --image=busybox /bin/sh
+If you don't see a command prompt, try pressing enter.
+/ # wget --spider --timeout=1 nginx
+Connecting to nginx (10.68.7.183:80)
+wget: download timed out
+
+# 而带有access=true标签的Pod可以访问nginx服务
+$ kubectl run busy2 --rm -it --labels="access=true" --image=busybox /bin/sh
+If you don't see a command prompt, try pressing enter.
+/ # wget --spider --timeout=1 nginx
+Connecting to nginx (10.68.7.183:80)
+```
+
+### 示例策略解读
+
+``` bash
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ name: test-network-policy
+ namespace: default
+spec:
+ podSelector:
+ matchLabels:
+ role: db
+ policyTypes:
+ - Ingress
+ - Egress
+ ingress:
+ - from:
+ - ipBlock:
+ cidr: 172.17.0.0/16
+ except:
+ - 172.17.1.0/24
+ - namespaceSelector:
+ matchLabels:
+ project: myproject
+ - podSelector:
+ matchLabels:
+ role: frontend
+ ports:
+ - protocol: TCP
+ port: 6379
+ egress:
+ - to:
+ - ipBlock:
+ cidr: 10.0.0.0/24
+ ports:
+ - protocol: TCP
+ port: 5978
+```
+- 策略作用的对象Pods:default命名空间下带有`role=db`标签的Pod
+ - 内向流量策略
+ - 允许属于`172.17.0.0/16`网段但不属于`172.17.1.0/24`的源地址访问该对象Pods的TCP 6379端口
+ - 允许带有project=myprojects标签的namespace中所有Pod访问该对象Pods的TCP 6379端口
+ - 允许default命名空间下带有role=frontend标签的Pod访问该对象Pods的TCP 6379端口
+ - 拒绝其他所有主动访问该对象Pods的网络流量
+ - 外向流量策略
+ - 允许该对象Pods主动访问目的地址属于`10.0.0.0/24`网段且目的端口为TCP 5978的流量
+ - 拒绝该对象Pods其他所有主动外向网络流量
+
+### 使用场景
+
+参考阅读[ahmetb/kubernetes-network-policy-recipes](https://github.com/ahmetb/kubernetes-network-policy-recipes) 该项目举例一些使用NetworkPolicy的场景,并有形象的配图
+
+#### 拒绝其他namespaces访问服务
+
+
+
++ 场景1:你的k8s集群应用按照namespaces区分生产、测试环境,你要确保生产环境不会受到测试环境错误访问影响
++ 场景2:你的k8s集群有多租户应用采用namespaces区分的,你要确保多租户之间的应用隔离
+
+在你需要隔离的命名空间创建如下策略:
+
+``` bash
+kind: NetworkPolicy
+apiVersion: networking.k8s.io/v1
+metadata:
+ namespace: your-ns
+ name: deny-other-namespaces
+spec:
+ podSelector:
+ matchLabels:
+ ingress:
+ - from:
+ - podSelector: {}
+```
+
+#### 允许外部访问服务
+
++ 场景:暴露特定Pod的特定端口给外部访问
+
+
+
+``` bash
+# 创建示例应用待暴露服务
+$ kubectl run web --image=nginx --labels=app=web --port 80 --expose
+
+# 创建网络策略
+kind: NetworkPolicy
+apiVersion: networking.k8s.io/v1
+metadata:
+ name: web-allow-external
+spec:
+ podSelector:
+ matchLabels:
+ app: web
+ ingress:
+ - from: []
+ ports:
+ - protocol: TCP
+ port: 80
+```
diff --git a/docs/guide/nfs-server.md b/docs/guide/nfs-server.md
new file mode 100644
index 0000000..1efdc29
--- /dev/null
+++ b/docs/guide/nfs-server.md
@@ -0,0 +1,81 @@
+## 创建 NFS 服务器
+
+NFS 允许系统将其目录和文件共享给网络上的其他系统。通过 NFS,用户和应用程序可以访问远程系统上的文件,就象它们是本地文件一样。
+
+### 安装
+Ubuntu 16.04 键入以下命令安装 NFS 服务器:
+
+``` bash
+apt install nfs-kernel-server
+```
+
+### 配置
+编辑`/etc/exports`文件添加需要共享目录,每个目录的设置独占一行,编写格式如下:
+
+`NFS共享目录路径 客户机IP或者名称(参数1,参数2,...,参数n)`
+
+例如:
+
+``` bash
+/home *(ro,sync,insecure,no_root_squash)
+/share 192.168.1.*(rw,sync,insecure,no_subtree_check,no_root_squash)
+```
+| 参数 | 说明 |
+| :- | :- |
+| ro | 只读访问 |
+| rw | 读写访问 |
+| sync | 所有数据在请求时写入共享 |
+| async | nfs在写入数据前可以响应请求 |
+| secure | nfs通过1024以下的安全TCP/IP端口发送 |
+| insecure | nfs通过1024以上的端口发送 |
+| wdelay | 如果多个用户要写入nfs目录,则归组写入(默认) |
+| no_wdelay | 如果多个用户要写入nfs目录,则立即写入,当使用async时,无需此设置 |
+| hide | 在nfs共享目录中不共享其子目录 |
+| no_hide | 共享nfs目录的子目录 |
+| subtree_check | 如果共享/usr/bin之类的子目录时,强制nfs检查父目录的权限(默认) |
+| no_subtree_check | 不检查父目录权限 |
+| all_squash | 共享文件的UID和GID映射匿名用户anonymous,适合公用目录 |
+| no_all_squash | 保留共享文件的UID和GID(默认) |
+| root_squash | root用户的所有请求映射成如anonymous用户一样的权限(默认) |
+| no_root_squash | root用户具有根目录的完全管理访问权限 |
+| anonuid=xxx | 指定nfs服务器/etc/passwd文件中匿名用户的UID |
+| anongid=xxx | 指定nfs服务器/etc/passwd文件中匿名用户的GID |
+
++ 注1:尽量指定主机名或IP或IP段最小化授权可以访问NFS 挂载的资源的客户端
++ 注2:经测试参数insecure必须要加,否则客户端挂载出错mount.nfs: access denied by server while mounting
+
+### 启动
+
+配置完成后,您可以在终端提示符后运行以下命令来启动 NFS 服务器:
+
+``` bash
+systemctl start nfs-kernel-server.service
+```
+
+### 客户端挂载
+
+Ubuntu 16.04,首先需要安装 `nfs-common` 包
+
+``` bash
+apt install nfs-common
+```
+CentOS 7, 需要安装 `nfs-utils` 包
+
+``` bash
+yum install nfs-utils
+```
+
+使用 mount 命令来挂载其他机器共享的 NFS 目录。可以在终端提示符后输入以下类似的命令:
+
+``` bash
+mount example.hostname.com:/ubuntu /local/ubuntu
+```
+挂载点 /local/ubuntu 目录必须已经存在。而且在 /local/ubuntu 目录中没有文件或子目录。
+
+另一个挂载NFS 共享的方式就是在 /etc/fstab 文件中添加一行。该行必须指明 NFS 服务器的主机名、服务器输出的目录名以及挂载 NFS 共享的本机目录。
+
+以下是在 /etc/fstab 中的常用语法:
+
+``` bash
+example.hostname.com:/ubuntu /local/ubuntu nfs rsize=8192,wsize=8192,timeo=14,intr
+```
diff --git a/docs/guide/prometheus.md b/docs/guide/prometheus.md
new file mode 100644
index 0000000..ef8f783
--- /dev/null
+++ b/docs/guide/prometheus.md
@@ -0,0 +1,151 @@
+# Prometheus
+随着`heapster`项目停止更新并慢慢被`metrics-server`取代,集群监控这项任务也将最终转移。`prometheus`的监控理念、数据结构设计其实相当精简,包括其非常灵活的查询语言;但是对于初学者来说,想要在k8s集群中实践搭建一套相对可用的部署却比较麻烦,由此还产生了不少专门的项目(如:[prometheus-operator](https://github.com/coreos/prometheus-operator)),本文介绍使用`helm chart`部署集群的prometheus监控。
+- `helm`已成为`CNCF`独立托管项目,预计会更加流行起来
+
+## 前提
+
+- 安装 helm:以本项目[安全安装helm](helm.md)为例
+- 安装 [kube-dns](kubedns.md)
+
+## 准备
+
+安装目录概览 `ll /etc/ansible/manifests/prometheus`
+
+``` bash
+drwx------ 3 root root 4096 Jun 3 22:42 grafana/
+-rw-r----- 1 root root 67875 Jun 4 22:47 grafana-dashboards.yaml
+-rw-r----- 1 root root 690 Jun 4 09:34 grafana-settings.yaml
+-rw-r----- 1 root root 1105 May 30 16:54 prom-alertrules.yaml
+-rw-r----- 1 root root 474 Jun 5 10:04 prom-alertsmanager.yaml
+drwx------ 3 root root 4096 Jun 2 21:39 prometheus/
+-rw-r----- 1 root root 294 May 30 18:09 prom-settings.yaml
+```
+- 目录`prometheus/`和`grafana/`即官方的helm charts,可以使用`helm fetch --untar stable/prometheus` 和 `helm fetch --untar stable/grafana`下载,本安装不会修改任何官方charts里面的内容,这样方便以后跟踪charts版本的更新
+- `prom-settings.yaml`:个性化prometheus安装参数,比如禁用PV,禁用pushgateway,设置nodePort等
+- `prom-alertrules.yaml`:配置告警规则
+- `prom-alertsmanager.yaml`:配置告警邮箱设置等
+- `grafana-settings.yaml`:个性化grafana安装参数,比如用户名密码,datasources,dashboardProviders等
+- `grafana-dashboards.yaml`:预设置dashboard
+
+## 安装
+
+``` bash
+$ source ~/.bashrc
+$ cd /etc/ansible/manifests/prometheus
+# 安装 prometheus chart,如果你的helm安装没有启用tls证书,请忽略--tls参数
+$ helm install --tls \
+ --name monitor \
+ --namespace monitoring \
+ -f prom-settings.yaml \
+ -f prom-alertsmanager.yaml \
+ -f prom-alertrules.yaml \
+ prometheus
+# 安装 grafana chart
+$ helm install --tls \
+ --name grafana \
+ --namespace monitoring \
+ -f grafana-settings.yaml \
+ -f grafana-dashboards.yaml \
+ grafana
+```
+
+## 验证安装
+
+``` bash
+# 查看相关pod和svc
+$ kubectl get pod,svc -n monitoring
+NAME READY STATUS RESTARTS AGE
+grafana-54dc76d47d-2mk55 1/1 Running 0 1m
+monitor-prometheus-alertmanager-6d9d9b5b96-w57bk 2/2 Running 0 2m
+monitor-prometheus-kube-state-metrics-69f5d56f49-fh9z7 1/1 Running 0 2m
+monitor-prometheus-node-exporter-55bwx 1/1 Running 0 2m
+monitor-prometheus-node-exporter-k8sb2 1/1 Running 0 2m
+monitor-prometheus-node-exporter-kxlr9 1/1 Running 0 2m
+monitor-prometheus-node-exporter-r5dx8 1/1 Running 0 2m
+monitor-prometheus-server-5ccfc77dff-8h9k6 2/2 Running 0 2m
+
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+grafana NodePort 10.68.74.242 80:39002/TCP 1m
+monitor-prometheus-alertmanager NodePort 10.68.69.105 80:39001/TCP 2m
+monitor-prometheus-kube-state-metrics ClusterIP None 80/TCP 2m
+monitor-prometheus-node-exporter ClusterIP None 9100/TCP 2m
+monitor-prometheus-server NodePort 10.68.248.94 80:39000/TCP 2m
+```
+
+- 访问prometheus的web界面:`http://$NodeIP:39000`
+- 访问alertmanager的web界面:`http://$NodeIP:39001`
+- 访问grafana的web界面:`http://$NodeIP:39002` (默认用户密码 admin:admin,可在web界面修改)
+
+## 管理操作
+
+- 升级(修改配置):修改配置请在`prom-settings.yaml` `prom-alertsmanager.yaml` 等文件中进行,保存后执行:
+``` bash
+# 修改prometheus
+$ helm upgrade --tls monitor -f prom-settings.yaml -f prom-alertsmanager.yaml -f prom-alertrules.yaml prometheus
+# 修改grafana
+$ helm upgrade --tls grafana -f grafana-settings.yaml -f grafana-dashboards.yaml grafana
+```
+- 回退:具体可以参考`helm help rollback`文档
+``` bash
+$ helm rollback --tls monitor [REVISION]
+```
+- 删除
+``` bash
+$ helm del --tls monitor --purge
+$ helm del --tls grafana --purge
+```
+
+## 验证告警
+
+- 修改`prom-alertsmanager.yaml`文件中邮件告警为有效的配置内容,并使用 helm upgrade更新安装
+- 手动临时关闭 master 节点的 kubelet 服务,等待几分钟看是否有告警邮件发送
+
+``` bash
+# 在 master 节点运行
+$ systemctl stop kubelet
+```
+
+## [可选] 配置钉钉告警
+
+- 创建钉钉群,获取群机器人 webhook 地址
+
+使用钉钉创建群聊以后可以方便设置群机器人,【群设置】-【群机器人】-【添加】-【自定义】-【添加】,然后按提示操作即可,参考 https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.666d4a97eCG7XA&treeId=257&articleId=105735&docType=1
+
+上述配置好群机器人,获得这个机器人对应的Webhook地址,记录下来,后续配置钉钉告警插件要用,格式如下
+
+```
+https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxx
+```
+
+- 创建钉钉告警插件,参考 http://theo.im/blog/2017/10/16/release-prometheus-alertmanager-webhook-for-dingtalk/
+
+``` bash
+# 编辑修改文件中 access_token=xxxxxx 为上一步你获得的机器人认证 token
+$ vi /etc/ansible/manifests/prometheus/dingtalk-webhook.yaml
+# 运行插件
+$ kubectl apply -f /etc/ansible/manifests/prometheus/dingtalk-webhook.yaml
+```
+
+- 修改 alertsmanager 告警配置后,更新 helm prometheus 部署,成功后如上节测试告警发送
+
+``` bash
+# 修改 alertsmanager 告警配置
+$ cd /etc/ansible/manifests/prometheus
+$ vi prom-alertsmanager.yaml
+# 增加 receiver dingtalk,然后在 route 配置使用 receiver: dingtalk
+ receivers:
+ - name: dingtalk
+ webhook_configs:
+ - send_resolved: false
+ url: http://webhook-dingtalk.monitoring.svc.cluster.local:8060/dingtalk/webhook1/send
+# ...
+# 更新 helm prometheus 部署
+$ helm upgrade --tls monitor -f prom-settings.yaml -f prom-alertsmanager.yaml -f prom-alertrules.yaml prometheus
+```
+
+## 下一步
+
+- 继续了解prometheus查询语言和配置文件
+- 继续了解prometheus告警规则,编写适合业务应用的告警规则
+- 继续了解grafana的dashboard编写,本项目参考了部分[feisky的模板](https://grafana.com/orgs/feisky/dashboards)
+如果对以上部分有心得总结,欢迎分享贡献在项目中。
diff --git a/docs/guide/rollingupdateWithZeroDowntime.md b/docs/guide/rollingupdateWithZeroDowntime.md
new file mode 100644
index 0000000..f2fc37f
--- /dev/null
+++ b/docs/guide/rollingupdateWithZeroDowntime.md
@@ -0,0 +1,215 @@
+## 1、前言
+在当下微服务架构盛行的时代,用户希望应用程序时时刻刻都是可用,为了满足不断变化的新业务,需要不断升级更新应用程序,有时可能需要频繁的发布版本。实现"零停机"、“零感知”的持续集成(Continuous Integration)和持续交付/部署(Continuous Delivery)应用程序,一直都是软件升级换代不得不面对的一个难题和痛点,也是一种追求的理想方式,也是DevOps诞生的目的。
+## 2、滚动发布
+把一次完整的发布过程,合理地分成多个批次,每次发布一个批次,**成功后**,再发布下一个批次,最终完成所有批次的发布。在整个滚动过程期间,保证始终有可用的副本在运行,从而平滑的发布新版本,实现**零停机(without an outage)**、用户**零感知**,是一种非常主流的发布方式。由于其自动化程度比较高,通常需要复杂的发布工具支撑,而k8s可以完美的胜任这个任务。
+## 3、k8s滚动更新机制
+**k8s创建副本应用程序的最佳方法就是部署(Deployment),部署自动创建副本集(ReplicaSet),副本集可以精确地控制每次替换的Pod数量,从而可以很好的实现滚动更新**。具体来说,k8s每次使用一个新的副本控制器(replication controller)来替换已存在的副本控制器,从而始终使用一个新的Pod模板来替换旧的pod模板。
+>大致步骤如下:
+>1. 创建一个新的replication controller。
+>2. 增加或减少pod副本数量,直到满足当前批次期望的数量。
+>3. 删除旧的replication controller。
+
+## 4、演示
+>使用kubectl更新一个已部署的应用程序,并模拟回滚。为了方便分析,将应用程序的pod副本数量设置为10。
+``` bash
+$ kubectl run busy --image=busybox:1.28.4 sleep 36000000 --replicas=10
+```
+### 4.1. 发布微服务
+- 当前服务状态查看
+``` bash
+# 查看部署列表
+root@kube-aio:~# kubectl get deploy busy
+NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
+busy 10 10 10 10 5m
+
+# 查看正在运行的pod
+root@kube-aio:~# kubectl get pod | grep busy
+busy-794c95f5d7-56b6w 1/1 Running 0 5m
+busy-794c95f5d7-8ddjr 1/1 Running 0 5m
+busy-794c95f5d7-8zm8r 1/1 Running 0 5m
+busy-794c95f5d7-9hjhp 1/1 Running 0 5m
+busy-794c95f5d7-df2r2 1/1 Running 0 5m
+busy-794c95f5d7-fsn94 1/1 Running 0 5m
+busy-794c95f5d7-k4w8r 1/1 Running 0 5m
+busy-794c95f5d7-lsmgb 1/1 Running 0 5m
+busy-794c95f5d7-rg8kw 1/1 Running 0 5m
+busy-794c95f5d7-xpxxt 1/1 Running 0 5m
+
+# 通过pod描述,查看应用程序的当前映像版本
+root@kube-aio:~# kubectl describe pod busy-794c95f5d7-56b6w |grep Image
+ Image: busybox:1.28.4
+ Image ID: docker-pullable://busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
+```
+- 升级镜像版本到1.29
+ - 为了更清晰看到更新过程,可另开一个窗口使用`$ watch kubectl get deployment busy`实时查看变化
+``` bash
+$ kubectl set image deployments/busy busy=busybox:1.29
+```
+### 4.2. 验证发布
+``` bash
+# 检查rollout状态
+root@kube-aio:~# kubectl rollout status deployments/busy
+deployment "busy" successfully rolled out
+
+# 检查pod详情
+root@kube-aio:~# kubectl describe pod busy-665cdb7b-44jnt |grep Image
+ Image: busybox:1.29
+ Image ID: docker-pullable://busybox@sha256:cb63aa0641a885f54de20f61d152187419e8f6b159ed11a251a09d115fdff9bd
+```
+从上面可以看到,镜像已经升级到1.29版本
+### 4.3. 回滚发布
+``` bash
+# 回滚发布
+root@kube-aio:~# kubectl rollout undo deployments/busy
+deployment.apps "busy"
+
+# 回滚完成
+root@kube-aio:~# kubectl rollout status deployments/busy
+deployment "busy" successfully rolled out
+
+# 镜像又回退到1.28.4 版本
+root@kube-aio:~# kubectl describe pod busy-794c95f5d7-4x9bn |grep Image
+ Image: busybox:1.28.4
+ Image ID: docker-pullable://busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
+```
+
+到目前为止,整个滚动发布工作就圆满完成了!!!
+**那么如果我们想回滚到指定版本呢?答案是k8s完美支持,并且还可以通过资源文件进行配置保留的历史版次量**。由于篇幅有限,感兴趣的朋友,可以自己下去实战,回滚命令如下:
+```javascript
+kubectl rollout undo deployment/busy --to-revision=<版次>
+```
+## 5、原理
+k8s精确地控制着整个发布过程,分批次有序地进行着滚动更新,直到把所有旧的副本全部更新到新版本。实际上,k8s是通过两个参数来精确地控制着每次滚动的pod数量:
+
+>* **`maxSurge` 滚动更新过程中运行操作期望副本数的最大pod数,可以为绝对数值(eg:5),但不能为0;也可以为百分数(eg:10%)。**
+>* **`maxUnavailable` 滚动更新过程中不可用的最大pod数,可以为绝对数值(eg:5),但不能为0;也可以为百分数(eg:10%)。**
+
+如果未指定这两个可选参数,则k8s会使用默认配置:
+``` bash
+root@kube-aio:~# kubectl get deploy busy -o yaml
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ annotations:
+ deployment.kubernetes.io/revision: "3"
+ creationTimestamp: 2018-08-19T02:42:56Z
+ generation: 3
+ labels:
+ run: busy
+ name: busy
+ namespace: default
+ resourceVersion: "199461"
+ selfLink: /apis/extensions/v1beta1/namespaces/default/deployments/busy
+ uid: 93fde307-a359-11e8-a93b-525400c61543
+spec:
+ progressDeadlineSeconds: 600
+ replicas: 10
+ revisionHistoryLimit: 10
+ selector:
+ matchLabels:
+ run: busy
+ strategy:
+ rollingUpdate:
+ maxSurge: 1 # 滚动更新中最多超过预期值的 pod数
+ maxUnavailable: 1 # 滚动更新中最多不可用的 pod数
+ type: RollingUpdate
+...
+```
+### 5.1. 浅析部署概况
+``` bash
+# 初始状态
+root@kube-aio:~# kubectl get deploy busy
+NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
+busy 10 10 10 10 1h
+
+# 再做一遍回退
+root@kube-aio:~# kubectl rollout undo deploy busy
+deployment.apps "busy"
+
+# 更新过程1
+root@kube-aio:~# kubectl get deploy busy
+NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
+busy 10 11 2 9 1h
+
+# 更新过程2
+root@kube-aio:~# kubectl get deploy busy
+NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
+busy 10 11 4 9 1h
+
+# 更新过程3
+root@kube-aio:~# kubectl get deploy busy
+NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
+busy 10 11 6 9 1h
+
+# 更新结束
+root@kube-aio:~# kubectl get deploy busy
+NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
+busy 10 10 10 10 1h
+```
+>* `DESIRED` 最终期望处于READY状态的副本数
+>* `CURRENT` 当前的副本总数
+>* `UP-TO-DATE` 当前完成更新的副本数
+>* `AVAILABLE` 当前可用的副本数
+
+当前的副本总数:10(DESIRED) + 1(maxSurge) = 11,所以CURRENT为11。
+当前可用的副本数:10(DESIRED) - 1(maxUnavailable) = 9,所以AVAILABLE为9。
+
+### 5.2. 浅析部署详情
+
+``` bash
+root@kube-aio:~# kubectl describe deploy busy
+Name: busy
+Namespace: default
+CreationTimestamp: Sun, 19 Aug 2018 12:27:19 +0800
+Labels: run=busy
+Annotations: deployment.kubernetes.io/revision=2
+Selector: run=busy
+Replicas: 10 desired | 10 updated | 10 total | 10 available | 0 unavailable
+StrategyType: RollingUpdate
+MinReadySeconds: 0
+RollingUpdateStrategy: 1 max unavailable, 1 max surge
+Pod Template:
+ Labels: run=busy
+ Containers:
+ busy:
+ Image: busybox:1.29
+ Port:
+ Host Port:
+ Args:
+ sleep
+ 3600000
+ Environment:
+ Mounts:
+ Volumes:
+Conditions:
+ Type Status Reason
+ ---- ------ ------
+ Available True MinimumReplicasAvailable
+ Progressing True NewReplicaSetAvailable
+OldReplicaSets:
+NewReplicaSet: busy-84cb46955d (10/10 replicas created)
+Events:
+ Type Reason Age From Message
+ ---- ------ ---- ---- -------
+ Normal ScalingReplicaSet 1m deployment-controller Scaled up replica set busy-9669c8599 to 10
+ Normal ScalingReplicaSet 46s deployment-controller Scaled up replica set busy-84cb46955d to 1
+ Normal ScalingReplicaSet 46s deployment-controller Scaled down replica set busy-9669c8599 to 9
+ Normal ScalingReplicaSet 46s deployment-controller Scaled up replica set busy-84cb46955d to 2
+ Normal ScalingReplicaSet 43s deployment-controller Scaled down replica set busy-9669c8599 to 8
+ Normal ScalingReplicaSet 43s deployment-controller Scaled up replica set busy-84cb46955d to 3
+ Normal ScalingReplicaSet 43s deployment-controller Scaled down replica set busy-9669c8599 to 7
+ Normal ScalingReplicaSet 43s deployment-controller Scaled up replica set busy-84cb46955d to 4
+ Normal ScalingReplicaSet 40s deployment-controller Scaled down replica set busy-9669c8599 to 6
+ Normal ScalingReplicaSet 28s (x12 over 40s) deployment-controller (combined from similar events): Scaled down replica set busy-9669c8599 to 0
+```
+整个滚动过程是通过控制两个副本集来完成的,新的副本集:busy-84cb46955d;旧的副本集:busy-9669c8599 。
+理想状态下的滚动过程:
+>1. 创建新副本集,并为其分配1个新版本的pod。
+>2. 通知旧副本集,销毁1个旧版本的pod。
+>3. 当旧副本销毁成功后,通知新副本集,再新增1个新版本的pod;当新副本创建成功后,通知旧副本再减少1个pod。
+>只要销毁成功,新副本集就会创造新的pod,一直循环,直到旧的副本集pod数量为0。
+### 5.4 总结
+**`无论理想还是不理想,k8s最终都会使应用程序全部更新到期望状态,都会始终保持最大的副本总数和可用副本总数的不变性!!!`**
+
+[阅读原文](http://www.cnblogs.com/justmine/p/8688828.html)
+
diff --git a/docs/mixes/01.fix_kubelet_annoymous_access.md b/docs/mixes/01.fix_kubelet_annoymous_access.md
new file mode 100644
index 0000000..965dbd4
--- /dev/null
+++ b/docs/mixes/01.fix_kubelet_annoymous_access.md
@@ -0,0 +1,24 @@
+# 修复kubelet默认允许匿名访问
+
+kubelet默认启动参数`--anonymous-auth=true`风险非常大,黑客可以在集群中植入挖坑程序,甚至通过这个漏洞获取宿主系统root权限。感谢 `cqspirit` [PR #192](https://github.com/gjmzj/kubeasz/pull/192) 提醒
+
+## 关于漏洞的危害
+
+据我所知k8s v1.5+ 所有版本的kubelet组件的默认启动参数是允许匿名访问kubelet的(默认的大坑),你可以使用如下命令检查你的集群:
+`curl -sk https://$NodeIP:10250/runningpods/`
+- 如果返回了运行的pod信息,说明是允许匿名访问的,
+- 如果返回`Unauthorized`,说明是安全的
+
+部分关于该漏洞的讨论参考如下:
+- [kubelet-exploit](https://github.com/kayrus/kubelet-exploit)
+- [Kubernetes-From-Container-To-Cluster](https://raesene.github.io/blog/2016/10/08/Kubernetes-From-Container-To-Cluster/)
+- [Analysis of a Kubernetes hack -- Backdooring through kubelet](https://www.reddit.com/r/netsec/comments/847994/analysis_of_a_kubernetes_hack_backdooring_through/)
+
+## 漏洞的修复
+
+最新代码已经修复,参考[官方文档说明](https://kubernetes.io/docs/admin/kubelet-authentication-authorization/),已有集群可以登陆`deploy`节点操作如下:
+``` bash
+$ cd /etc/ansible
+$ git pull origin master
+$ ansible-playbook 22.upgrade.yml -t restart_master,restart_node
+```
diff --git a/docs/mixes/DoneList.md b/docs/mixes/DoneList.md
new file mode 100644
index 0000000..0aefad6
--- /dev/null
+++ b/docs/mixes/DoneList.md
@@ -0,0 +1,55 @@
+## 前言
+
+`kubeasz`项目开始于`2017.11`,半年多时间以来,从最开始单一的ansible部署脚本朝着提供部署高可用 K8S集群的完整解决方案的目标不断前进,接下去项目的发展需要各位的共同参与和贡献,希望越做越好,为国内k8s学习、实践者提供更多帮助。
+
+### 项目已完成部分
+
+
+
+ 类型
+ 描述
+ 备注
+
+
+ 集群部署
+ 服务器基础安全加固与参数优化
+ 已完成
+
+
+ 基础服务
+ 集群监控告警-prometheus
+ 已完成基础,待优化
+
+
+ 应用服务
+ jenkins集成
+ 已完成
+
+
+ 集群部署
+ kube-router网络插件
+ 已完成
+
+
+ 基础服务
+ metrics server
+ 已完成
+
+
+ 集群部署
+ ipvs代理模式跟进
+ 已完成
+
+
+ 集群部署
+ cilium网络插件
+ 已完成
+
+
+ 集群部署
+ 集群内时间同步-Chrony
+ 已完成
+
+
+
+
diff --git a/docs/mixes/HowToContribute.md b/docs/mixes/HowToContribute.md
new file mode 100644
index 0000000..2c7ffc7
--- /dev/null
+++ b/docs/mixes/HowToContribute.md
@@ -0,0 +1,19 @@
+# 为项目`kubeasz`提交`pull request`
+
+首先请核对下本地git config配置的用户名和邮箱与你github上的注册用户和邮箱一致,否则即使`pull request`被接受,贡献者列表中也看不到自己的名字,设置命令:
+
+``` bash
+$ git config --global user.email "you@example.com"
+$ git config --global user.name "Your Name"
+```
+
+- 1.登陆github,在本项目页面点击`fork`到自己仓库
+- 2.clone 自己的仓库到本地:`git clone https://github.com/xxx/kubeasz.git`
+- 3.在 master 分支添加原始仓库为上游分支:`git remote add upstream https://github.com/gjmzj/kubeasz.git`
+- 4.在本地新建开发分支:`git checkout -b dev`
+- 5.在开发分支修改代码并提交:`git add .`, `git commit -am 'xx变更说明'`
+- 6.切换至 master 分支,同步原始仓库:`git checkout master`, `git pull upstream master`
+- 7.切换至 dev 分支,合并本地 master 分支(已经和原始仓库同步),可能需要解冲突:`git checkout dev`, `git merge master`
+- 8.提交本地 dev 分支到自己的远程 dev 仓库:`git push origin dev`
+- 9.在github自己仓库页面,点击`Compare & pull request`给原始仓库发 pull request 请求
+- a.等待原作者回复(接受/拒绝)
diff --git a/docs/mixes/LICENSE b/docs/mixes/LICENSE
new file mode 100644
index 0000000..48a6741
--- /dev/null
+++ b/docs/mixes/LICENSE
@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "{}" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+ Copyright 2017 jmgao
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/docs/mixes/TodoList.md b/docs/mixes/TodoList.md
new file mode 100644
index 0000000..80729af
--- /dev/null
+++ b/docs/mixes/TodoList.md
@@ -0,0 +1,31 @@
+## 前言
+
+`kubeasz`项目开始于`2017.11`,从最开始单一的ansible部署脚本朝着提供部署高可用 K8S集群的完整解决方案的目标不断前进;项目的发展需要各位的共同参与和贡献,希望越做越好,为国内k8s学习、实践者提供更多帮助。
+现在是很好的时间节点,梳理项目的`TodoList`/`Milestones`,以下是现有的一些想法,欢迎大家在[这里](https://github.com/gjmzj/kubeasz/issues/188)参与讨论。
+
+### 项目 TodoList
+
+
+
+ 类型
+ 描述
+ 备注
+
+
+ 基础服务
+ 集群监控告警-prometheus
+ 已完成基础,待优化
+
+
+ 应用服务
+ 服务治理-istio跟进
+
+
+
+ 基础服务
+ ingress-nginx
+ 待集成manifests及使用文档
+
+
+
+
diff --git a/docs/mixes/branch.md b/docs/mixes/branch.md
new file mode 100644
index 0000000..ec6aee7
--- /dev/null
+++ b/docs/mixes/branch.md
@@ -0,0 +1,18 @@
+# 项目分支说明
+
+- 为减小维护压力,避免产生混淆,精简项目分支为:`master`, `dev`, `closed`, `release-0`; 原分支`v1.9`取消(可以直接更新至master分支),原分支`v1.8`更名为 `closed`并停止更新。
+- 更新:
+ - kubeasz 0.6 及之前的版本已合并至 release-0 分支,后续停止更新,只做 bug 修复
+ - kubeasz 1.x 版本将在 master 分支更新发布
+
+## 项目分支与百度网盘离线包关系
+
+- `master` 分支目前支持k8s v1.8/v1.9/v1.10/v1.11/v1.12/v1.13 的集群安装,请使用 `k8s.1-x-x.tar.gz` 的安装包
+- `dev` 分支同 `master`的离线安装包
+- `closed` 分支请使用分享链接目录`old/`下的 `k8s.18x.tar.gz` 的安装包(已停止更新)
+
+## 更新频率和内容
+
+- `master` 为最新稳定分支,更新相对频繁:**相关文档**,**功能特性**,BUG修复,组件更新
+- `dev` 为开发测试分支,不一定稳定,更新频繁:**相关文档**,**功能特性**,BUG修复,组件更新
+- `closed` 分支已停止更新:仅重要BUG修复,留档保存使用
diff --git a/docs/mixes/donate.md b/docs/mixes/donate.md
new file mode 100644
index 0000000..d705986
--- /dev/null
+++ b/docs/mixes/donate.md
@@ -0,0 +1,8 @@
+# 捐赠
+
+如果觉得本项目对您有帮助,请小小鼓励下项目作者,谢谢!
+
+支付宝码(左)和微信钱包码(右)
+
+ 
+
diff --git a/docs/op/AddMaster.md b/docs/op/AddMaster.md
new file mode 100644
index 0000000..5905456
--- /dev/null
+++ b/docs/op/AddMaster.md
@@ -0,0 +1,43 @@
+## 增加 kube-master 节点
+
+注意:目前仅支持按照本项目`多主模式`(hosts.m-masters.example/hosts.cloud.example)部署的`k8s`集群增加`master`节点
+
+新增`kube-master`节点大致流程为:
+- [可选]新节点安装 chrony 时间同步
+- 新节点预处理 prepare
+- 新节点安装 docker 服务
+- 新节点安装 kube-master 服务
+- 新节点安装 kube-node 服务
+- 新节点安装网络插件相关
+- 禁止业务 pod调度到新master节点
+- 更新配置 haproxy 负载均衡并重启
+
+### 操作步骤
+
+首先配置 ssh 免密码登陆新增节点,然后执行 (假设待增加节点为 192.168.1.11):
+
+``` bash
+$ easzctl add-master 192.168.1.11
+```
+
+### 验证
+
+``` bash
+# 在新节点master 服务状态
+$ systemctl status kube-apiserver
+$ systemctl status kube-controller-manager
+$ systemctl status kube-scheduler
+
+# 查看新master的服务日志
+$ journalctl -u kube-apiserver -f
+
+# 查看集群节点,可以看到新 master节点 Ready, 并且禁止了POD 调度功能
+$ kubectl get node
+NAME STATUS ROLES AGE VERSION
+192.168.1.1 Ready,SchedulingDisabled 3h v1.9.3
+192.168.1.2 Ready,SchedulingDisabled 3h v1.9.3
+192.168.1.3 Ready 3h v1.9.3
+192.168.1.4 Ready 3h v1.9.3
+192.168.1.11 Ready,SchedulingDisabled 2h v1.9.3 # 新增 master节点
+```
+
diff --git a/docs/op/AddNode.md b/docs/op/AddNode.md
new file mode 100644
index 0000000..4ca27e3
--- /dev/null
+++ b/docs/op/AddNode.md
@@ -0,0 +1,29 @@
+## 增加 kube-node 节点
+
+新增`kube-node`节点大致流程为:
+- [可选]新节点安装 chrony 时间同步
+- 新节点预处理 prepare
+- 新节点安装 docker 服务
+- 新节点安装 kube-node 服务
+- 新节点安装网络插件相关
+
+### 操作步骤
+
+首先配置 ssh 免密码登陆新增节点,然后执行 (假设待增加节点为 192.168.1.11):
+
+``` bash
+$ easzctl add-node 192.168.1.11
+```
+
+### 验证
+
+``` bash
+# 验证新节点状态
+$ kubectl get node
+
+# 验证新节点的网络插件calico 或flannel 的Pod 状态
+$ kubectl get pod -n kube-system
+
+# 验证新建负载能否调度到新节点,略
+```
+
diff --git a/docs/op/ChangeVIP.md b/docs/op/ChangeVIP.md
new file mode 100644
index 0000000..31175b6
--- /dev/null
+++ b/docs/op/ChangeVIP.md
@@ -0,0 +1,107 @@
+# 更改高可用 `Master IP`
+
+**WARNING:** 更改集群的 `Master VIP`操作有风险,不建议在生产环境直接操作,此文档实践一个修改的操作流程,帮助理解整个集群运行架构和 `kubeasz`的部署逻辑,请在测试环境操作练手。
+**BUG:** 目前该操作只适用于集群网络选用`calico`,如果使用`flannel`操作变更后会出现POD地址分配错误的BUG。
+
+首先分析大概操作思路:
+
+- 修改`/etc/ansible/hosts`里面的配置项`MASTER_IP` `KUBE_APISERVER`
+- 修改LB节点的keepalive的配置,重启keepalived服务
+- 修改kubectl/kube-proxy的配置文件,使用新VIP地址更新api-server地址
+- 重新生成master证书,hosts字段包含新VIP地址
+- 修改kubelet的配置文件(kubelet的配置文件和证书是由bootstrap机制自动生成的)
+ - 删除kubelet.kubeconfig
+ - 删除集群所有node 节点
+ - 所有节点重新bootstrap
+
+## 变更前状态验证
+
+``` bash
+$ kubectl get cs,node,pod -o wide
+NAME STATUS MESSAGE ERROR
+controller-manager Healthy ok
+scheduler Healthy ok
+etcd-2 Healthy {"health":"true"}
+etcd-0 Healthy {"health":"true"}
+etcd-1 Healthy {"health":"true"}
+
+NAME STATUS ROLES AGE VERSION EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
+192.168.1.41 Ready,SchedulingDisabled 2h v1.10.0 Ubuntu 16.04.3 LTS 4.4.0-97-generic docker://18.3.0
+192.168.1.42 Ready,SchedulingDisabled 2h v1.10.0 Ubuntu 16.04.3 LTS 4.4.0-97-generic docker://18.3.0
+192.168.1.43 Ready 2h v1.10.0 Ubuntu 16.04.3 LTS 4.4.0-97-generic docker://18.3.0
+192.168.1.44 Ready 2h v1.10.0 Ubuntu 16.04.3 LTS 4.4.0-98-generic docker://18.3.0
+192.168.1.45 Ready 2h v1.10.0 Ubuntu 16.04.3 LTS 4.4.0-98-generic docker://18.3.0
+
+NAME READY STATUS RESTARTS AGE IP NODE
+busy-5d6b6b5d4b-8wxkp 1/1 Running 0 17h 172.20.135.133 192.168.1.41
+busy-5d6b6b5d4b-fcmkp 1/1 Running 0 17h 172.20.135.128 192.168.1.41
+busy-5d6b6b5d4b-ptvd7 1/1 Running 0 17h 172.20.135.136 192.168.1.41
+nginx-768979984b-ncqbp 1/1 Running 0 17h 172.20.135.137 192.168.1.41
+
+# 查看待变更集群 Master VIP
+$ kubectl cluster-info
+Kubernetes master is running at https://192.168.1.39:8443
+```
+
+## 变更操作
+
+- `ansible playbook`可以使用tags来控制只允许部分任务执行,这里为简化操作没有细化,在deploy节点具体操作如下:
+
+``` bash
+# 1.修改/etc/ansible/hosts 配置项MASTER_IP,KUBE_APISERVER
+
+# 2.删除集群所有node节点,等待重新bootstrap
+$ kubectl get node |grep Ready|awk '{print $1}' |xargs kubectl delete node
+
+# 3.重置keepalived 和修改kubectl/kube-proxy/bootstrap配置
+$ ansible-playbook 01.prepare.yml
+
+# 4.删除旧master证书
+$ ansible kube-master -m file -a 'path=/etc/kubernetes/ssl/kubernetes.pem state=absent'
+
+# 5.删除旧kubelet配置文件
+$ ansible all -m file -a 'path=/etc/kubernetes/kubelet.kubeconfig state=absent'
+
+# 6.重新配置启动master节点
+$ ansible-playbook 04.kube-master.yml
+
+# 7.重新配置启动node节点
+$ ansible-playbook 05.kube-node.yml
+```
+
+## 变更后验证
+
+``` bash
+$ kubectl get cs,node,pod -o wide
+NAME STATUS MESSAGE ERROR
+scheduler Healthy ok
+controller-manager Healthy ok
+etcd-2 Healthy {"health":"true"}
+etcd-1 Healthy {"health":"true"}
+etcd-0 Healthy {"health":"true"}
+
+NAME STATUS ROLES AGE VERSION EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
+192.168.1.41 Ready,SchedulingDisabled 4m v1.10.0 Ubuntu 16.04.3 LTS 4.4.0-97-generic docker://18.3.0
+192.168.1.42 Ready,SchedulingDisabled 4m v1.10.0 Ubuntu 16.04.3 LTS 4.4.0-97-generic docker://18.3.0
+192.168.1.43 Ready 3m v1.10.0 Ubuntu 16.04.3 LTS 4.4.0-97-generic docker://18.3.0
+192.168.1.44 Ready 3m v1.10.0 Ubuntu 16.04.3 LTS 4.4.0-98-generic docker://18.3.0
+192.168.1.45 Ready 3m v1.10.0 Ubuntu 16.04.3 LTS 4.4.0-98-generic docker://18.3.0
+
+NAME READY STATUS RESTARTS AGE IP NODE
+busy-5d6b6b5d4b-25hfr 1/1 Running 0 5m 172.20.237.64 192.168.1.43
+busy-5d6b6b5d4b-cdzb5 1/1 Running 0 5m 172.20.145.192 192.168.1.44
+busy-5d6b6b5d4b-m2rf7 1/1 Running 0 5m 172.20.26.131 192.168.1.45
+nginx-768979984b-2ngww 1/1 Running 0 5m 172.20.145.193 192.168.1.44
+
+# 查看集群master VIP已经变更
+$ kubectl cluster-info
+Kubernetes master is running at https://192.168.1.40:8443
+```
+
+## 小结
+
+本示例操作演示了多主多节点k8s集群变更`Master VIP`的操作,有助于理解整个集群组件架构和`kubeasz`的安装逻辑,小结如下:
+
+- 变更操作不影响集群已运行的业务POD,但是操作过程中业务会中断
+- 已运行POD会重新调度到各node节点,如果业务POD量很大,短时间内会对集群造成压力
+- 不建议在生成环境直接操作,本示例演示说明为主
diff --git a/docs/op/ch_apiserver_cert.md b/docs/op/ch_apiserver_cert.md
new file mode 100644
index 0000000..626d554
--- /dev/null
+++ b/docs/op/ch_apiserver_cert.md
@@ -0,0 +1,41 @@
+# 修改 APISERVER(MASTER)证书
+
+`kubeasz` 创建集群后,APISERVER(MASTER)证书默认 CN 包含如下`域名`和`IP`:参见`roles/kube-master/templates/kubernetes-csr.json.j2`
+
+```
+ "hosts": [
+ "127.0.0.1",
+ "{{ MASTER_IP }}",
+ "{{ inventory_hostname }}",
+ "{{ CLUSTER_KUBERNETES_SVC_IP }}",
+{% for HOST in MASTER_CERT_HOSTS %}
+ "{{ HOST }}",
+{% endfor %}
+ "kubernetes",
+ "kubernetes.default",
+ "kubernetes.default.svc",
+ "kubernetes.default.svc.cluster",
+ "kubernetes.default.svc.cluster.local"
+ ],
+```
+
+有的时候(比如apiserver地址通过边界防火墙的NAT转换成公网IP访问,或者需要添加公网域名访问)我们需要在 APISERVER(MASTER)证书中添加一些`域名`或者`IP`,可以方便操作如下:
+
+## 1.修改配置文件`roles/kube-master/defaults/main.yml`
+
+``` bash
+# k8s 集群 master 节点证书配置,可以添加多个ip和域名(比如增加公网ip和域名)
+MASTER_CERT_HOSTS:
+ - "10.1.1.1"
+ - "k8s.test.io"
+ #- "61.182.11.41"
+ #- "www.test.com"
+```
+
+## 2.执行新证书生成即可
+
+``` bash
+$ ansible-playbook 04.kube-master.yml -t change_cert
+```
+
+- 注:新证书生效无需重启`kube-apiserver.service`服务
diff --git a/docs/op/change_ip_allinone.md b/docs/op/change_ip_allinone.md
new file mode 100644
index 0000000..3c0c5d8
--- /dev/null
+++ b/docs/op/change_ip_allinone.md
@@ -0,0 +1,17 @@
+# 修改AIO 部署的IP
+前两天在项目[ISSUES #201](https://github.com/gjmzj/kubeasz/issues/201)看到有人提:`在虚拟机A装了allinone,并搭建一套开发环境,我想通过copy A出来一套B然后交给别人测试`,觉得这个场景蛮有用,就写了这个文档和对应的脚本,希望对各位有帮助,也可以熟悉kubeasz的安装逻辑。
+
+首先,因为kubeasz创建的集群都是TLS双向认证的,所以修改host ip地址比想象中要复杂很多。具体步骤可以参考[脚本](../../tools/change_ip_aio.yml)中的注释内容。
+
+- 本操作指南仅适用于测试交流
+
+## 操作步骤
+前提 :一个运行正常的allinone部署在虚机,关机后复制给别人使用,新虚机开机后如果需要修改IP,请执行如下步骤:
+
+- 0.拉取最新项目代码:`git pull origin master`
+- 1.修改ansible hosts文件:`sed -i 's/$OLD_IP/$NEW_IP/g' /etc/ansible/hosts`
+- 2.配置ssh免密码登陆:`ssh-copy-id $NEW_IP` 按提示完成
+- 3.检查下修改是否成功,并且能够成功执行 `ansible all -m ping`
+- 4.以上步骤完成后,执行 `ansible-playbook /etc/ansible/tools/change_ip_aio.yml`
+
+执行成功即可,请自己验证原先集群中各应用是否正常。
diff --git a/docs/op/change_k8s_network.md b/docs/op/change_k8s_network.md
new file mode 100644
index 0000000..0d402fc
--- /dev/null
+++ b/docs/op/change_k8s_network.md
@@ -0,0 +1,31 @@
+# 替换k8s集群的网络插件
+
+有时候我们在测试环境的k8s集群中希望试用多种网络插件(calico/flannel/kube-router),又不希望每测试一次就全部清除集群然后重建,那么可能这个文档适合你。
+- WARNNING:重新安装k8s网络插件会短暂中断已有运行在k8s上的服务
+ - 请在熟悉kubeasz的安装流程和k8s网络插件安装流程的基础上谨慎操作
+ - 如果k8s集群已经运行庞大业务pod,重装网络插件时会引起所有pod的删除、重建,短时间内将给apiserver带来压力,可能引起master节点夯住
+ - 确保没有裸pod 运行(因为最后需要删除所有pod 重建,裸pod 不会重建),即所有pod 都是由 deploy/daemonset/statefulset 等创建;
+
+## 替换流程
+
+kubeasz使用标准cni方式安装k8s集群的网络插件;cni负载创建容器网卡和IP分配(IPAM),不同的网络插件(calico,flannel等)创建容器网卡和IP分配方式不一样,所以在替换网络插件时候需要现有pod全部删除,然后自动按照新网络插件的方式重建pod网络;请参考[k8s网络插件章节](../setup/06-install_network_plugin.md)。
+
+### 替换操作
+
+替换网络插件操作很简单,只要两步:
+- 1.修改ansible hosts文件指定新网络插件
+- 2.执行替换脚本 `ansible-playbook /etc/ansible/tools/change_k8s_network.yml`
+
+对照脚本`change_k8s_network.yml` 讲解下大致流程为:
+a.根据实际运行情况,删除现有网络组件的daemonset pod
+b.如果现有组件是kube-router 需要进行一些额外清理
+c.暂停node相关服务,后面才可以进一步清理iptables等
+d.执行旧网络插件相关清理
+e.重新开启node相关服务
+f.安装新网络插件
+g.删除所有运行pod,然后等待自动重建
+
+## 验证新网络插件
+
+参照[calico](../setup/network-plugin/calico.md) [cilium](../setup/network-plugin/cilium.md) [flannel](../setup/network-plugin/flannel.md) [kube-router](../setup/network-plugin/kube-router.md)
+
diff --git a/docs/op/clean_one_node.md b/docs/op/clean_one_node.md
new file mode 100644
index 0000000..e532898
--- /dev/null
+++ b/docs/op/clean_one_node.md
@@ -0,0 +1,42 @@
+# 删除节点
+
+本文档所指删除的节点是指使用kubeasz项目安装的节点角色(可能是kube-master, kube-node, etcd, lb节点)
+
+- 警告:此操作将清理单个node节点,包含k8s集群可能使用的数据,特别的:如果有pod使用了本地存储类型,请自行判断重要性
+
+## 删除流程解释
+
+- 0.判断待删除节点不是 etcd/master 组的唯一节点,否则不允许删除
+- 1.待删除节点可能是kube-node节点,因此先执行`kubectl drain`,如果不是忽略执行报错
+- 2.参照`99.clean.yml`脚本方式删除节点可能的服务和配置,忽略执行报错
+- 3.待删除节点可能是kube-node节点,执行`kubectl delete node`, 如果不是忽略执行报错
+- 4.修改ansible hosts,移除删除节点
+
+## 删除操作
+
+可以使用以下三种方式删除节点(i.e. 192.168.1.1)
+
+``` bash
+# 1.推荐使用 easzctl 工具
+$ easzctl clean-node 192.168.1.1
+
+# 2.ansible-playbook 带参数执行如下
+$ ansible-playbook /etc/ansible/tools/clean_one_node.yml -e NODE_TO_DEL=192.168.1.1
+
+# 3.ansible-playbook 不带参数执行,然后根据提示输入/确认
+$ ansible-playbook /etc/ansible/tools/clean_one_node.yml
+```
+
+## 验证
+
+- 验证删除节点上是否相关服务均已停止
+- 验证 ansible hosts 文件中已删除节点
+
+## Debug
+
+如果出现清理失败,类似报错:`... Device or resource busy: '/var/run/docker/netns/xxxxxxxxxx'`,需要手动umount该目录后重新清理
+
+``` bash
+$ umount /var/run/docker/netns/xxxxxxxxxx
+$ ansible-playbook /etc/ansible/tools/clean_one_node.yml
+```
diff --git a/docs/op/cluster_restore.md b/docs/op/cluster_restore.md
new file mode 100644
index 0000000..967e580
--- /dev/null
+++ b/docs/op/cluster_restore.md
@@ -0,0 +1,129 @@
+# K8S 集群备份与恢复
+
+虽然 K8S 集群可以配置成多主多节点的高可用的部署,还是有必要了解下集群的备份和容灾恢复能力;在高可用k8s集群中 etcd集群保存了整个集群的状态,因此这里的备份与恢复重点就是:
+
+- 从运行的etcd集群备份数据到磁盘文件
+- 从etcd备份文件恢复数据到运行的etcd集群,然后据此重建整个集群
+
+## 前提
+
+k8s 集群可能因为软硬件故障或者误操作出现了不可自愈的问题,这个时候需要考虑集群从备份中恢复重建;使用kubeasz项目创建的集群如需恢复前提如下:
+
+- 集群正常状态下的etcd 备份文件(etcd V3数据)
+- 创建集群时使用的 CA证书相关文件
+- 创建集群时使用的 ansible hosts文件
+
+## 备份与恢复手动操作说明
+
+首先用kubeasz 搭建一个测试集群,部署几个测试deployment,验证集群各项正常后,进行一次备份:
+
+- 1.在一个etcd节点上执行数据备份,把产生的备份文件`snapshot.db`复制到所有etcd集群节点
+
+``` bash
+$ mkdir -p /backup/k8s/ && cd /backup/k8s
+$ ETCDCTL_API=3 etcdctl snapshot save snapshot.db
+```
+
+- 2.在deploy节点把 CA证书相关备份出来
+
+``` bash
+$ mkdir -p /backup/k8s/ && cp /etc/kubernetes/ssl/ca* /backup/k8s/
+```
+
+- 3.在deploy节点清理集群,模拟集群完全崩溃
+
+``` bash
+$ ansible-playbook /etc/ansible/99.clean.yml
+```
+
+- 4.在deploy节点开始一步步重建集群
+
+``` bash
+# 恢复原集群的CA 证书相关
+$ mkdir -p /etc/kubernetes/ssl/ && cp /backup/k8s/* /etc/kubernetes/ssl/
+
+# 然后执行集群恢复步骤,安装至 kube-node完成阶段
+$ cd /etc/ansible
+$ ansible-playbook 01.prepare.yml
+$ ansible-playbook 02.etcd.yml
+$ ansible-playbook 03.docker.yml
+$ ansible-playbook 04.kube-master.yml
+$ ansible-playbook 05.kube-node.yml
+
+# 以上步骤验证正常后,停止etcd集群服务,并清空新etcd集群数据目录
+$ ansible etcd -m service -a 'name=etcd state=stopped'
+$ asnible etcd -m file -a 'name=/var/lib/etcd/member/ state=absent'
+```
+
+- 5.手动分别登陆每个etcd节点进行数据备份恢复,每个etcd都要如下操作
+
+``` bash
+# 参照本etcd节点/etc/systemd/system/etcd.service的服务文件,替换如下{{}}中变量后执行
+$ cd /backup/k8s/
+$ ETCDCTL_API=3 etcdctl snapshot restore snapshot.db \
+ --name {{ NODE_NAME }} \
+ --initial-cluster {{ ETCD_NODES }} \
+ --initial-cluster-token etcd-cluster-0 \
+ --initial-advertise-peer-urls https://{{ inventory_hostname }}:2380
+
+# 以上执行完后,会生成{{ NODE_NAME }}.etcd的文件夹,将它里面的member 拷贝到etcd数据目录中
+$ cp -r {{ NODE_NAME }}.etcd/member /var/lib/etcd/
+
+$ systemctl restart etcd
+```
+
+- 6.在deploy节点执行网络重建
+
+``` bash
+$ ansible-playbook /etc/ansible/tools/change_k8s_network.yml
+```
+
+执行完之后,可以验证整个集群是否恢复正常,之前的测试应用部署是否全部恢复。
+
+- 参考:https://github.com/coreos/etcd/blob/master/Documentation/op-guide/recovery.md
+
+## 备份恢复自动脚本操作指南
+
+- 一.集群备份
+
+``` bash
+$ ansible-playbook /etc/ansible/23.backup.yml
+```
+
+执行完毕可以在目录 `/etc/ansible/roles/cluster-backup/files`下检查备份情况,示例如下:
+
+``` bash
+roles/cluster-backup/files/
+├── ca # 集群CA 相关备份
+│ ├── ca-config.json
+│ ├── ca.csr
+│ ├── ca-csr.json
+│ ├── ca-key.pem
+│ └── ca.pem
+├── hosts # ansible hosts备份
+│ ├── hosts # 最近的备份
+│ └── hosts-201807231642
+├── readme.md
+└── snapshot # etcd 数据备份
+ ├── snapshot-201807231642.db
+ └── snapshot.db # 最近的备份
+```
+
+- 二.模拟集群故障
+
+``` bash
+$ ansible-playbook /etc/ansible/99.clean.yml
+```
+
+**注意** 为了模拟集群彻底崩溃,这里清理整个集群;实际操作中,在有备份前提下,也建议彻底清理集群后再尝试去恢复
+
+- 三.集群恢复
+
+可以在 `roles/cluster-restore/defaults/main.yml` 文件中配置需要恢复的 etcd备份版本,默认使用最近一次备份
+
+``` bash
+$ ansible-playbook /etc/ansible/24.restore.yml
+$ ansible-playbook /etc/ansible/tools/change_k8s_network.yml
+```
+
+执行完成可以验证整个集群是否恢复如初!
diff --git a/docs/op/loadballance_ingress_nodeport.md b/docs/op/loadballance_ingress_nodeport.md
new file mode 100644
index 0000000..67d80fd
--- /dev/null
+++ b/docs/op/loadballance_ingress_nodeport.md
@@ -0,0 +1,54 @@
+# 配置负载转发 ingress nodeport
+
+向集群外暴露 ingress-controller 本身的服务端口(80/443/8080)一般有以下三种方法:
+
+- 1.部署ingress-controller时使用`hostNetwork: true`,这样就可以直接使用上述端口,可能与host已listen端口冲突
+- 2.部署ingress-controller时使用`LoadBalancer`类型服务,需要集群支持`LoadBalancer`
+- 3.部署ingress-controller时使用`nodePort`类型服务,然后在集群外使用 haproxy/f5 等配置 virtual server 集群
+
+本文档讲解使用 haproxy 配置 ingress的 VS 集群,前提是`多主多节点集群`并且配置了自建`lb`节点
+
+## 1.配置 lb 参数开启转发 ingress nodeport
+
+``` bash
+# 编辑 roles/lb/defaults/main.yml,配置如下变量
+INGRESS_NODEPORT_LB: "yes"
+INGRESS_TLS_NODEPORT_LB: "yes"
+```
+
+## 2.重新配置启动LB节点服务
+
+``` bash
+$ ansible-playbook /etc/ansible/roles/lb/lb.yml
+```
+
+## 3.验证 lb 节点的 haproxy 服务配置 `/etc/haproxy/haproxy.cfg` 包含如下配置
+
+``` bash
+... 前文省略
+listen kube-master
+ bind 0.0.0.0:8443
+ mode tcp
+ option tcplog
+ balance roundrobin
+ server 192.168.1.1 192.168.1.1:6443 check inter 2000 fall 2 rise 2 weight 1
+ server 192.168.1.2 192.168.1.2:6443 check inter 2000 fall 2 rise 2 weight 1
+
+listen ingress-node
+ bind 0.0.0.0:80
+ mode tcp
+ option tcplog
+ balance roundrobin
+ server 192.168.1.3 192.168.1.3:23456 check inter 2000 fall 2 rise 2 weight 1
+ server 192.168.1.4 192.168.1.4:23456 check inter 2000 fall 2 rise 2 weight 1
+
+listen ingress-node-tls
+ bind 0.0.0.0:443
+ mode tcp
+ option tcplog
+ balance roundrobin
+ server 192.168.1.3 192.168.1.3:23457 check inter 2000 fall 2 rise 2 weight 1
+ server 192.168.1.4 192.168.1.4:23457 check inter 2000 fall 2 rise 2 weight 1
+```
+
+验证成功后,我们可以方便的去做[配置ingress](../guide/ingress.md)和[配置https ingress](../guide/ingress-tls.md)实验了。
diff --git a/docs/op/op-etcd.md b/docs/op/op-etcd.md
new file mode 100644
index 0000000..f843582
--- /dev/null
+++ b/docs/op/op-etcd.md
@@ -0,0 +1,53 @@
+# 管理 etcd 集群
+
+Etcd 集群支持在线改变集群成员节点,可以增加、修改、删除成员节点;不过改变成员数量仍旧需要满足集群成员多数同意原则(quorum),另外请记住集群成员数量变化的影响:
+
+- 增加 etcd 集群节点, 提高集群稳定性
+- 增加 etcd 集群节点, 提高集群读性能(所有节点数据一致,客户端可以从任意节点读取数据)
+- 增加 etcd 集群节点, 降低集群写性能(所有节点数据一致,每一次写入会需要所有节点数据同步)
+
+## 备份 etcd 数据
+
+可以根据需要进行定期备份(使用 crontab),或者手动在任意正常 etcd 节点上执行备份:
+
+``` bash
+# snapshot备份
+$ ETCDCTL_API=3 etcdctl snapshot save backup.db
+# 查看备份
+$ ETCDCTL_API=3 etcdctl --write-out=table snapshot status backup.db
+```
+
+## etcd 集群节点操作
+
+首先确认配置 ssh 免密码登陆,然后执行 (假设待操作节点为 192.168.1.11):
+
+- 增加 etcd 节点:`$ easzctl add-etcd 192.168.1.11` (注意:增加 etcd 还需要根据提示输入集群内唯一的 NODE_NAME)
+- 删除 etcd 节点:`$ easzctl del-etcd 192.168.1.11`
+
+### 验证 etcd 集群
+
+``` bash
+# 登陆任意etcd节点验证etcd集群状态
+$ export ETCDCTL_API=3
+$ etcdctl member list
+
+# 验证所有etcd节点服务状态和日志
+$ systemctl status etcd
+$ journalctl -u etcd -f
+```
+
+### 重置 k8s 连接 etcd 参数
+
+上述步骤验证成功,确认新etcd集群工作正常后,可以重新配置运行apiserver,以让 k8s 集群能够识别新的etcd集群:
+
+``` bash
+# 重启 master 节点服务
+$ ansible-playbook /etc/ansible/04.kube-master.yml -t restart_master
+
+# 验证 k8s 能够识别新 etcd 集群
+$ kubectl get cs
+```
+
+## 参考
+
+- 官方文档 https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/runtime-configuration.md
diff --git a/docs/op/op-index.md b/docs/op/op-index.md
new file mode 100644
index 0000000..dce778e
--- /dev/null
+++ b/docs/op/op-index.md
@@ -0,0 +1,14 @@
+# 集群运维管理指南 operation guide
+
+- [集群添加 NODE 节点](AddNode.md)
+- [集群添加 MASTER 节点](AddMaster.md)
+- [集群添加 ETCD 节点](AddEtcd.md)
+- [升级 K8S 版本](upgrade.md)
+- [修改多主集群VIP地址](ChangeVIP.md)
+- [修改AIO部署的系统IP](change_ip_allinone.md)
+- [集群删除单个节点](clean_one_node.md)
+- [替换集群使用的网络插件](change_k8s_network.md)
+- [集群备份与恢复](cluster_restore.md)
+- [设置只读权限 kubeconfig](readonly_kubectl.md)
+- [修改 APISERVER 证书](ch_apiserver_cert.md)
+- [配置负载转发 ingress nodeport](loadballance_ingress_nodeport.md)
diff --git a/docs/op/readonly_kubectl.md b/docs/op/readonly_kubectl.md
new file mode 100644
index 0000000..73d52ff
--- /dev/null
+++ b/docs/op/readonly_kubectl.md
@@ -0,0 +1,70 @@
+# 配置 kubectl 只读访问权限
+
+默认 k8s 集群安装后配置的 kubectl 客户端拥有所有的管理权限,而有时候我们需要把只读权限分发给普通开发人员,本文档将创建一个只读权限的kubectl 配置文档 kubeconfig。
+
+## 创建
+
+- 备份下原先 admin 权限的 kubeconfig 文件:`mv ~/.kube ~/.kubeadmin`
+- 执行 `ansible-playbook /etc/ansible/roles/deploy/create-read-kubeconfig.yml`,成功后查看~/.kube/config 即为只读权限
+
+## 讲解
+
+对照文件`/etc/ansible/roles/deploy/create-read-kubeconfig.yml`,创建主要包括三个步骤:
+
+- 创建 group:read rbac 权限
+- 创建 read 用户证书和私钥
+- 创建 kubeconfig
+
+### read rbac 权限
+
+所有权限控制魔法在`k8s`中由`rbac`实现,所谓`read`权限类似于集群自带的`clusterrole view`,具体查看:
+
+`kubectl get clusterrole view -o yaml`
+
+`read`权限配置`roles/deploy/files/read-group-rbac.yaml`是在`clusterrole view`基础上增加了若干读权限(Nodes/Persistent Volume Claims)
+
+### read 用户证书
+
+准备 read 证书请求:`read-csr.json`
+
+``` bash
+{
+ "CN": "read",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "group:read",
+ "OU": "System"
+ }
+ ]
+}
+```
+- 注意: O `group:read`,kube-apiserver 收到该证书后将请求的 Group 设置为`group:read`;之前步骤创建的 ClusterRoleBinding `read-clusterrole-binding`将 Group `group:read`与 ClusterRole `read-clusterrole`绑定,从而实现只读权限。
+
+### read kubeconfig
+
+kubeconfig 为与apiserver交互使用的认证配置文件,如脚本步骤需要:
+
+- 设置集群参数,指定CA证书和apiserver地址
+- 设置客户端认证参数,指定使用read证书和私钥
+- 设置上下文参数,指定使用cluster集群和用户read
+- 设置指定默认上下文
+
+创建完成后生成默认配置文件为 `~/.kube/config`
+
+## 恢复 admin 权限
+
+- 可以恢复之前备份的`~/.kubeadmin`文件:`mv ~/.kube ~/.kuberead && mv ~/.kubeadmin ~/.kube`
+- 或者直接执行 `ansible-playbook /etc/ansible/roles/deploy/create-admin-kubeconfig.yml`
+
+## 参考
+
+- [Using RBAC Authorization](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)
+- [A Read Only Kubernetes Dashboard](https://blog.cowger.us/2018/07/03/a-read-only-kubernetes-dashboard.html)
diff --git a/docs/op/upgrade.md b/docs/op/upgrade.md
new file mode 100644
index 0000000..5657f30
--- /dev/null
+++ b/docs/op/upgrade.md
@@ -0,0 +1,47 @@
+## k8s 集群升级
+
+集群升级存在一定风险,请谨慎操作。
+
+- 项目分支`master`安装的集群可以在k8s 1.8/1.9/1.10/1.11/1.12/1.13 任意小版本、大版本间升级
+- 项目分支`closed`(已停止更新)安装的集群目前只能进行小版本1.8.x的升级
+
+### 备份etcd数据
+
+- 升级前对 etcd数据做备份,在任意 etcd节点上执行:
+
+``` bash
+# snapshot备份
+$ ETCDCTL_API=3 etcdctl snapshot save backup.db
+# 查看备份
+$ ETCDCTL_API=3 etcdctl --write-out=table snapshot status backup.db
+```
+- `kubeasz`项目也可以方便执行 `ansible-playbook /etc/ansible/23.backup.yml`,详情阅读文档[备份恢复](cluster_restore.md)
+
+### 快速k8s版本升级
+
+快速升级是指只升级`k8s`版本,比较常见如`Bug修复` `重要特性发布`时使用。
+
+- 首先去官网release下载待升级的k8s版本,例如`https://dl.k8s.io/v1.11.5/kubernetes-server-linux-amd64.tar.gz`
+- 解压下载的tar.gz文件,找到如下`kube*`开头的二进制,复制替换deploy节点目录`/etc/ansible/bin`对应文件
+ - kube-apiserver
+ - kube-controller-manager
+ - kubectl
+ - kubelet
+ - kube-proxy
+ - kube-scheduler
+- 在deploy节点执行`ansible-playbook -t upgrade_k8s 22.upgrade.yml`即可完成k8s 升级,不会中断业务应用
+
+### 其他升级说明
+
+其他升级是指升级k8s组件包括:`etcd版本` `docker版本`,一般不需要用到,以下仅作说明。
+
+- 1.下载所有组件相关新的二进制解压并替换 `/etc/ansible/bin/` 目录下文件
+
+- 2.升级 etcd: `ansible-playbook -t upgrade_etcd 02.etcd.yml`,**注意:etcd 版本只能升级不能降低!**
+
+- 3.升级 docker (建议使用k8s官方支持的docker稳定版本)
+ - 如果可以接受短暂业务中断,执行 `ansible-playbook -t upgrade_docker 03.docker.yml`
+ - 如果要求零中断升级,执行 `ansible-playbook -t download_docker 03.docker.yml`,然后手动执行如下
+ - 待升级节点,先应用`kubectl cordon`和`kubectl drain`命令迁移业务pod
+ - 待升级节点执行 `systemctl restart docker`
+ - 恢复节点可调度 `kubectl uncordon`
diff --git a/docs/practice/es_cluster.md b/docs/practice/es_cluster.md
new file mode 100644
index 0000000..39b2ffd
--- /dev/null
+++ b/docs/practice/es_cluster.md
@@ -0,0 +1,184 @@
+# Elasticsearch 部署实践
+
+`Elasticsearch`是目前全文搜索引擎的首选,它可以快速地储存、搜索和分析海量数据;也可以看成是真正分布式的高效数据库集群;`Elastic`的底层是开源库`Lucene`;封装并提供了`REST API`的操作接口。
+
+## 单节点 docker 测试安装
+
+``` bash
+cat > es-start.sh << EOF
+#!/bin/bash
+
+sysctl -w vm.max_map_count=262144
+
+docker run --detach \
+ --name es01 \
+ -p 9200:9200 -p 9300:9300 \
+ -e "discovery.type=single-node" \
+ -e "bootstrap.memory_lock=true" --ulimit memlock=-1:-1 \
+ --ulimit nofile=65536:65536 \
+ --volume /srv/elasticsearch/data:/usr/share/elasticsearch/data \
+ --volume /srv/elasticsearch/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
+ jmgao1983/elasticsearch:6.4.0
+EOF
+```
+
+执行`sh es-start.sh`后,就在本地运行了。
+
+- 验证 docker 镜像运行情况
+
+``` bash
+root@docker-ts:~# docker ps -a
+CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
+171f3fecb596 jmgao1983/elasticsearch:6.4.0 "/usr/local/bin/do..." 2 hours ago Up 2 hours 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp es01
+```
+
+- 验证 es 健康检查
+
+``` bash
+root@docker-ts:~# curl http://127.0.0.1:9200/_cat/health
+epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
+1535523956 06:25:56 docker-es green 1 1 0 0 0 0 0 0 - 100.0%
+```
+
+## 在 k8s 上部署 Elasticsearch 集群
+
+在生产环境下,Elasticsearch 集群由不同的角色节点组成:
+
+- master 节点:参与主节点选举,不存储数据;建议3个以上,维护整个集群的稳定可靠状态
+- data 节点:不参与选主,负责存储数据;主要消耗磁盘,内存
+- client 节点:不参与选主,不存储数据;负责处理用户请求,实现请求转发,负载均衡等功能
+
+这里使用`helm chart`来部署 (https://github.com/helm/charts/tree/master/incubator/elasticsearch)
+
+- 1.安装 helm: 以本项目[安全安装helm](../guide/helm.md)为例
+- 2.准备 PV: 以本项目[K8S 集群存储](../setup/08-cluster-storage.md)创建`nfs`动态 PV 为例
+ - 编辑配置文件:roles/cluster-storage/defaults/main.yml
+
+``` bash
+storage:
+ nfs:
+ enabled: "yes"
+ server: "192.168.1.8"
+ server_path: "/share"
+ storage_class: "nfs-es"
+ provisioner_name: "nfs-provisioner-01"
+```
+
+ - 创建 nfs provisioner
+
+``` bash
+$ ansible-playbook /etc/ansible/roles/cluster-storage/cluster-storage.yml
+# 执行成功后验证
+$ kubectl get pod --all-namespaces |grep nfs-prov
+kube-system nfs-provisioner-01-6b7fbbf9d4-bh8lh 1/1 Running 0 1d
+```
+
+- 3.安装 elasticsearch chart
+
+``` bash
+$ cd /etc/ansible/manifests/es-cluster
+# 如果你的helm安装没有启用tls证书,请忽略以下--tls参数
+$ helm install --tls --name es-cluster --namespace elastic -f es-values.yaml elasticsearch
+```
+
+- 4.验证 es 集群
+
+``` bash
+# 验证k8s上 es集群状态
+$ kubectl get pod,svc -n elastic
+NAME READY STATUS RESTARTS AGE
+pod/es-cluster-elasticsearch-client-778df74c8f-7fj4k 1/1 Running 0 2m17s
+pod/es-cluster-elasticsearch-client-778df74c8f-skh8l 1/1 Running 0 2m3s
+pod/es-cluster-elasticsearch-data-0 1/1 Running 0 25m
+pod/es-cluster-elasticsearch-data-1 1/1 Running 0 11m
+pod/es-cluster-elasticsearch-master-0 1/1 Running 0 25m
+pod/es-cluster-elasticsearch-master-1 1/1 Running 0 12m
+pod/es-cluster-elasticsearch-master-2 1/1 Running 0 10m
+
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+service/es-cluster-elasticsearch-client NodePort 10.68.157.105 9200:29200/TCP,9300:29300/TCP 25m
+service/es-cluster-elasticsearch-discovery ClusterIP None 9300/TCP 25m
+
+# 验证 es集群本身状态
+$ curl $NODE_IP:29200/_cat/health
+1539335131 09:05:31 es-on-k8s green 7 2 0 0 0 0 0 0 - 100.0%
+
+$ curl $NODE_IP:29200/_cat/indices?v
+health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
+root@k8s401:/etc/ansible# curl 10.100.97.41:29200/_cat/nodes?
+172.31.2.4 27 80 5 0.09 0.11 0.21 mi - es-cluster-elasticsearch-master-0
+172.31.1.7 30 97 3 0.39 0.29 0.27 i - es-cluster-elasticsearch-client-778df74c8f-skh8l
+172.31.3.7 20 97 3 0.11 0.17 0.18 i - es-cluster-elasticsearch-client-778df74c8f-7fj4k
+172.31.1.5 8 97 5 0.39 0.29 0.27 di - es-cluster-elasticsearch-data-0
+172.31.2.5 8 80 3 0.09 0.11 0.21 di - es-cluster-elasticsearch-data-1
+172.31.1.6 18 97 4 0.39 0.29 0.27 mi - es-cluster-elasticsearch-master-2
+172.31.3.6 20 97 4 0.11 0.17 0.18 mi * es-cluster-elasticsearch-master-1
+```
+
+### es 性能压测
+
+如上已使用 chart 在 k8s上部署了 **7** 节点的 elasticsearch 集群;各位应该十分好奇性能怎么样;官方提供了压测工具[esrally](https://github.com/elastic/rally)可以方便的进行性能压测,这里省略安装和测试过程;压测机上执行:
+`esrally --track=http_logs --target-hosts="$NODE_IP:29200" --pipeline=benchmark-only --report-file=report.md`
+压测过程需要1-2个小时,部分压测结果如下:
+
+``` bash
+------------------------------------------------------
+ _______ __ _____
+ / ____(_)___ ____ _/ / / ___/_________ ________
+ / /_ / / __ \/ __ `/ / \__ \/ ___/ __ \/ ___/ _ \
+ / __/ / / / / / /_/ / / ___/ / /__/ /_/ / / / __/
+/_/ /_/_/ /_/\__,_/_/ /____/\___/\____/_/ \___/
+------------------------------------------------------
+
+| Lap | Metric | Task | Value | Unit |
+|------:|-------------------------------------:|-------------:|------------:|--------:|
+...
+| All | Min Throughput | index-append | 16903.2 | docs/s |
+| All | Median Throughput | index-append | 17624.4 | docs/s |
+| All | Max Throughput | index-append | 19382.8 | docs/s |
+| All | 50th percentile latency | index-append | 1865.74 | ms |
+| All | 90th percentile latency | index-append | 3708.04 | ms |
+| All | 99th percentile latency | index-append | 6379.49 | ms |
+| All | 99.9th percentile latency | index-append | 8389.74 | ms |
+| All | 99.99th percentile latency | index-append | 9612.84 | ms |
+| All | 100th percentile latency | index-append | 9861.02 | ms |
+| All | 50th percentile service time | index-append | 1865.74 | ms |
+| All | 90th percentile service time | index-append | 3708.04 | ms |
+| All | 99th percentile service time | index-append | 6379.49 | ms |
+| All | 99.9th percentile service time | index-append | 8389.74 | ms |
+| All | 99.99th percentile service time | index-append | 9612.84 | ms |
+| All | 100th percentile service time | index-append | 9861.02 | ms |
+| All | error rate | index-append | 0 | % |
+| All | Min Throughput | default | 0.66 | ops/s |
+| All | Median Throughput | default | 0.66 | ops/s |
+| All | Max Throughput | default | 0.66 | ops/s |
+| All | 50th percentile latency | default | 770131 | ms |
+| All | 90th percentile latency | default | 825511 | ms |
+| All | 99th percentile latency | default | 838030 | ms |
+| All | 100th percentile latency | default | 839382 | ms |
+| All | 50th percentile service time | default | 1539.4 | ms |
+| All | 90th percentile service time | default | 1635.39 | ms |
+| All | 99th percentile service time | default | 1728.02 | ms |
+| All | 100th percentile service time | default | 1736.2 | ms |
+| All | error rate | default | 0 | % |
+...
+```
+
+从测试结果看:集群的吞吐可以(k8s es-client pod还可以扩展);延迟略高一些(因为使用了nfs共享存储);整体效果不错。
+
+### 中文分词安装
+
+安装 ik 插件即可,可以自定义已安装ik插件的es docker镜像:创建如下 Dockerfile
+
+``` bash
+FROM jmgao1983/elasticsearch:6.4.0
+
+RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install \
+ --batch https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.4.0/elasticsearch-analysis-ik-6.4.0.zip \
+ && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
+```
+
+### 参考阅读
+
+1. [Elasticsearch 入门教程](http://www.ruanyifeng.com/blog/2017/08/elasticsearch.html)
+2. [Elasticsearch 压测方案之 esrally 简介](https://segmentfault.com/a/1190000011174694)
diff --git a/docs/practice/java_war_app.md b/docs/practice/java_war_app.md
new file mode 100644
index 0000000..89f7c7a
--- /dev/null
+++ b/docs/practice/java_war_app.md
@@ -0,0 +1,159 @@
+# JAVA WAR 应用迁移 K8S 实践
+
+初步思路是这样:应用代码与应用配置分离,应用代码打包成 docker 镜像存于内部 harbor 仓库,应用配置使用 configmap 挂载,这样不同的环境只需要修改 configmap 即可部署。
+
+- 使用 maven 把 java 应用代码打包成 xxx.war
+- 基于 tomcat 镜像和 xxx.war 做成应用 docker 镜像
+- 编写 k8s deployment 文件,在 pod 指定上述应用镜像,同时把应用配置做成 configmap 挂载到 pod 里
+
+经过多次尝试部署发现问题:configmap配置是可以挂载上去,但是会把目录下其他的文件删掉,而且tomcat 目录 webapps/xxxxx/下其他目录也消失了。原来是因为 tomcat 容器完全启动完成后才会解压 war包,而 configmap 配置文件是一开始就挂载上去了,导致失败。
+
+- 调整应用镜像打包过程:xxx.war 先解压后再进行应用镜像打包
+
+## 应用 gitlab CI/CD 集成
+
+- 在内部gitlab创建项目,上传应用java代码,同时在项目根目录下新加如下目录和文件,配置相应的 gitlab-runner 和 环境变量参数
+
+``` bash
+├── .app.yaml # k8s deployment 部署模板文件
+├── config.yaml # k8s configmap 配置模板文件
+├── dockerfiles
+│ └── Dockerfile # Dockerfile 文件
+├── .gitlab-ci.yml # gitlab ci 配置文件
+└── .ns.yaml # k8s namespace 和 imagePullSecrets的配置文件
+```
+### gitlab-ci 文件摘要
+
+``` bash
+variables:
+ PROJECT_NS: '$CI_PROJECT_NAMESPACE-$CI_JOB_STAGE'
+ APP_NAME: '$CI_PROJECT_NAME-$CI_COMMIT_REF_SLUG'
+
+stages:
+ - package
+ - beta
+
+job_package:
+ stage: package
+ tags:
+ - package-shell
+ only:
+ - master
+ - /^feature-.*$/
+ script:
+ - mvn clean install -Dmaven.test.skip=true
+ - unzip target/xxxx.war -d dockerfiles/project
+ - cd dockerfiles && docker build -t harbor.test.lo/project/$CI_PROJECT_NAME:$CI_PIPELINE_ID .
+ - docker login -u $HARBOR_USR -p $HARBOR_PWD harbor.test.lo
+ - docker push harbor.test.lo/project/$CI_PROJECT_NAME:$CI_PIPELINE_ID
+ - docker logout harbor.test.lo
+
+job_push_beta:
+ stage: beta
+ tags:
+ - beta-shell
+ only:
+ - master
+ - /^feature-.*$/
+ when: manual
+ script:
+ # 替换beta环境的参数配置
+ - sed -i "s/PROJECT_NS/$PROJECT_NS/g" config.yaml .app.yaml .ns.yaml
+ - sed -i "s/TemplateProject/$APP_NAME/g" config.yaml .app.yaml
+ - sed -i "s/DB_HOST/$BETA_DB_HOST/g" config.yaml
+ - sed -i "s/DB_PWD/$BETA_DB_PWD/g" config.yaml
+ - sed -i "s/APP_REP/$BETA_APP_REP/g" .app.yaml
+ - sed -i "s/ProjectImage/$CI_PROJECT_NAME:$CI_PIPELINE_ID/g" .app.yaml
+ #
+ - mkdir -p /opt/kube/$PROJECT_NS/$APP_NAME
+ - cp -f .ns.yaml config.yaml .app.yaml /opt/kube/$PROJECT_NS/$APP_NAME
+ - kubectl --kubeconfig=/etc/.beta/config apply -f .ns.yaml
+ - kubectl --kubeconfig=/etc/.beta/config apply -f config.yaml
+ - kubectl --kubeconfig=/etc/.beta/config apply -f .app.yaml
+
+# 生产部署与beta环境类同,这里省略
+```
+
+### Dockerfile 编写
+
+```
+FROM tomcat:8.5.33-jre8-alpine
+
+COPY . /usr/local/tomcat/webapps/
+
+# 设置tomcat日志使用的时区
+RUN sed -i 's/^JAVA_OPTS=.*webresources\"$/JAVA_OPTS=\"$JAVA_OPTS -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Duser.timezone=GMT+08\"/g' /usr/local/tomcat/bin/catalina.sh
+```
+
+### k8s deployment 配置举例
+
+```
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: TemplateProject
+ namespace: PROJECT_NS
+spec:
+ replicas: APP_REP
+ template:
+ metadata:
+ labels:
+ run: TemplateProject
+ spec:
+ containers:
+ - name: TemplateProject
+ image: harbor.test.lo/project/ProjectImage
+ imagePullPolicy: IfNotPresent
+ ports:
+ - containerPort: 8080
+ volumeMounts:
+ - name: db-config
+ mountPath: "/usr/local/tomcat/webapps/project/xxxx/yyyy/config/datasource.properties"
+ subPath: datasource.properties
+ imagePullSecrets:
+ - name: projectkey1
+ volumes:
+ - name: db-config
+ configMap:
+ name: TemplateProject-config
+ defaultMode: 0640
+ items:
+ - path: datasource.properties
+ key: datasource.properties
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ run: TemplateProject
+ name: TemplateProject
+ namespace: PROJECT_NS
+spec:
+ ports:
+ - port: 80
+ protocol: TCP
+ targetPort: 8080
+ selector:
+ run: TemplateProject
+ sessionAffinity: None
+```
+
+### k8s configmap 配置举例
+
+```
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: TemplateProject-config
+ namespace: PROJECT_NS
+data:
+ datasource.properties: |
+ dataSource.maxIdle = 5
+ dataSource.maxActive = 41
+ dataSource.driverClassName = com.mysql.jdbc.Driver
+ dataSource.url = jdbc:mysql://DB_HOST:8066/project?useUnicode=true&characterEncoding=utf-8
+ dataSource.username = username
+ dataSource.password = DB_PWD
+```
diff --git a/docs/practice/mariadb_cluster.md b/docs/practice/mariadb_cluster.md
new file mode 100644
index 0000000..b5e333a
--- /dev/null
+++ b/docs/practice/mariadb_cluster.md
@@ -0,0 +1,103 @@
+# Mariadb 数据库集群
+
+Mariadb 是从 MySQL 衍生出来的开源关系型数据库,目前兼容 mysql 5.7 版本;它也非常流行,拥有 Google Facebook 等重要企业用户。本文档介绍使用 helm charts 方式安装 mariadb cluster,仅供实践交流使用。
+
+## 前提条件
+
+- 已部署 k8s 集群,参考[这里](../setup/quickStart.md)
+- 已部署 helm,参考[这里](../guide/helm.md)
+- 集群提供持久性存储,参考[这里](../setup/08-cluster-storage.md)
+
+这里演示使用 nfs 动态存储,编辑修改 nfs 存储部分参数
+
+``` bash
+$ vi roles/cluster-storage/defaults/main.yml
+storage:
+ # nfs server 参数
+ nfs:
+ enabled: "yes" # 启用 nfs
+ server: "172.16.3.86" # 设置 nfs 服务器地址
+ server_path: "/data/nfs" # 设置共享目录
+ storage_class: "nfs-db" # 定义 storage_class,后面pvc要调用这个
+ provisioner_name: "nfs-provisioner-01" # 任意命名
+
+# 配置完成,保存退出,运行下面命令
+$ ansible-playbook /etc/ansible/roles/cluster-storage/cluster-storage.yml
+# 确认nfs provisioner pod
+$ kubectl get pod --all-namespaces |grep nfs
+kube-system nfs-provisioner-01-88694d78c-mrn7f 1/1 Running 0 6m
+```
+
+## mariadb charts 配置修改
+
+按照惯例,直接把 chart 下载到本地,然后把配置复制 values.yaml 出来进行修改,这样方便以后整体更新 chart,安装实际使用需要修改配置文件
+
+``` bash
+$ cd /etc/ansible/manifests/mariadb-cluster
+# 编辑 my-values.yaml 修改以下部分
+
+service:
+ type: NodePort # 方便集群外部访问
+ port: 3306
+ nodePort:
+ master: 33306 # 设置主库的nodePort
+ slave: 33307 # 设置从库的nodePort
+
+rootUser: # 设置 root 密码
+ password: test.c0m
+ forcePassword: true
+
+db: # 设置初始测试数据库
+ user: hello
+ password: hello
+ name: hello
+ forcePassword: true
+
+replication: # 设置主从复制
+ enabled: true
+ user: replicator
+ password: R4%forep11CAT0r
+ forcePassword: true
+
+master:
+ affinity: {}
+ antiAffinity: soft
+ tolerations: []
+ persistence:
+ enabled: true # 启用持久化存储
+ mountPath: /bitnami/mariadb
+ storageClass: "nfs-db" # 设置使用 nfs-db 存储类
+ annotations: {}
+ accessModes:
+ - ReadWriteOnce
+ size: 5Gi # 设置存储容量
+
+slave:
+ replicas: 1
+ affinity: {}
+ antiAffinity: soft
+ tolerations: []
+ persistence:
+ enabled: false # 从库这里没有启用持久性存储
+```
+
+## 安装
+
+使用 helm 安装
+
+``` bash
+$ cd /etc/ansible/manifests/mariadb-cluster
+$ helm install --name mariadb --namespace default -f my-values.yaml ./mariadb
+```
+
+## 验证
+
+``` bash
+$ kubectl get pod,svc | grep mariadb
+pod/mariadb-mariadb-master-0 1/1 Running 0 27m
+pod/mariadb-mariadb-slave-0 1/1 Running 0 29m
+
+service/mariadb NodePort 10.68.170.168 3306:33306/TCP 29m
+service/mariadb-mariadb-slave NodePort 10.68.151.95 3306:33307/TCP 29m
+```
+
diff --git a/docs/release-notes/kubeasz-0.1.0.md b/docs/release-notes/kubeasz-0.1.0.md
new file mode 100644
index 0000000..01e6fa1
--- /dev/null
+++ b/docs/release-notes/kubeasz-0.1.0.md
@@ -0,0 +1,33 @@
+## kubeasz-0.1.0 发布说明
+
+`kubeasz`项目第一个独立版本发布,使用`ansible playbook`自动化安装k8s集群(目前支持v1.8/v1.9/v1.10)和主要插件,方便部署和灵活配置集群;
+
+CHANGELOG:
+- 组件更新:
+ - kubernetes v1.10.4, v1.9.8, v1.8.12
+ - etcd v3.3.6
+- 安全更新:
+ - 修复kubelet匿名访问漏洞(感谢 cqspirit #192 提醒)
+- 功能更新:
+ - 增加helm安全部署及说明
+ - 增加prometheus部署及说明
+ - 增加jenkins部署及说明(感谢 lusyoe #208 )
+- 脚本更新:
+ - 精简 inventory(/etc/ansible/hosts)配置项
+ - 移动calico/flannel配置至对应的roles/defaults/main.yml
+ - 取消变量NODE_IP,使用内置变量inventory_hostname代替
+ - 取消lb组变量设置,自动完成
+ - 取消etcd相关集群变量设置,自动完成
+ - 增加集群版本K8S_VER变量,为兼容k8s v1.8安装
+ - 增加修改AIO部署的系统IP的脚本和说明(docs/op/change_ip_allinone.md)
+ - 增加设置node角色
+ - 修改OS安全加固脚本为可选安装
+- 其他:
+ - 修复calico-controller多网卡问题
+ - 修改manifests/apiserver参数兼容k8s v1.8
+ - 简化新增master/node节点步骤
+ - 优化ansible配置参数
+ - 更新 harbor 1.5.1及文档修复(感谢 lusyoe #224 )
+ - 更新 kube-dns 1.14.10
+ - 丰富dashboard文档( #182 )
+ - 修复selinux关闭( #194 )
diff --git a/docs/release-notes/kubeasz-0.2.0.md b/docs/release-notes/kubeasz-0.2.0.md
new file mode 100644
index 0000000..e8d5533
--- /dev/null
+++ b/docs/release-notes/kubeasz-0.2.0.md
@@ -0,0 +1,19 @@
+## kubeasz-0.2.0 发布说明
+
+CHANGELOG:
+- 组件更新:
+ - 增加新网络插件 kube-router,可在ansible hosts配置`CLUSTER_NETWORK="kube-router"`
+- 功能更新:
+ - 增加IPVS/LVS服务代理模式,比默认的kube-proxy服务代理更高效;在选择kube-router网络插件时配置`SERVICE_PROXY="IPVS"`
+ - 增加部署metrics-server,以替代heapster 提供metrics API
+ - 增加自动集成安装kube-dns/dashboard等组件,可在`roles/cluster-addon/defaults/main.yml`配置
+- 脚本更新:
+ - 增加删除单个节点脚本,docs/op/del_one_node.md
+ - 增加等待网络插件正常运行
+ - Bug fix: 更新99.clean.yml清理脚本,解决集群重装后cni地址分配问题 kubernetes #57280
+ - Bug fix: 从0.1.0版本升级时,kube-apiserver服务启动失败问题
+- 其他:
+ - 修改部分镜像拉取策略统一为:`imagePullPolicy: IfNotPresent`
+ - 新增metrics-server、cluster-addon文档
+ - 更新kube-router相关文档
+ - 更新集群升级说明文档 docs/op/upgrade.md
diff --git a/docs/release-notes/kubeasz-0.2.1.md b/docs/release-notes/kubeasz-0.2.1.md
new file mode 100644
index 0000000..b2ac198
--- /dev/null
+++ b/docs/release-notes/kubeasz-0.2.1.md
@@ -0,0 +1,18 @@
+## kubeasz-0.2.1 发布说明
+
+CHANGELOG:
+如果服务器能够使用内部yum源/apt源,但是无法访问公网情况下,请下载离线docker镜像完成集群安装:从百度云盘把`basic_images_kubeasz_x.y.tar.gz` 下载解压到项目`down`目录即可
+- 组件更新:
+ - 更新 coredns版本1.1.3
+- 功能更新:
+ - 集成网络插件(可选)使用离线docker镜像安装
+ - 集成其他插件(可选)使用离线docker镜像安装
+ - 增加切换集群网络插件的脚本
+- 文档更新:
+ - [快速指南](https://github.com/gjmzj/kubeasz/blob/master/docs/setup/quickStart.md)
+ - [安装规划](https://github.com/gjmzj/kubeasz/blob/master/docs/setup/00-planning_and_overall_intro.md)
+ - [切换网络](https://github.com/gjmzj/kubeasz/blob/master/docs/op/clean_k8s_network.md)
+- 其他:
+ - Bug fix: 清理集群时可能出现`Device or resource busy: '/var/run/docker/netns/xxxxxxx'`的错误,可手动umount后重新清理集群
+ - Bug fix: #239 harbor调整安装解压工具, 适配多系统 (#240)
+
diff --git a/docs/release-notes/kubeasz-0.2.2.md b/docs/release-notes/kubeasz-0.2.2.md
new file mode 100644
index 0000000..2714b74
--- /dev/null
+++ b/docs/release-notes/kubeasz-0.2.2.md
@@ -0,0 +1,22 @@
+## kubeasz-0.2.2 发布说明
+
+CHANGELOG:
+- 组件更新:
+ - k8s v1.11.0
+ - etcd v3.3.8
+ - docker 18.03.1-ce
+- 功能更新:
+ - 更新使用ipvs 配置及[说明文档](https://github.com/gjmzj/kubeasz/blob/master/docs/guide/ipvs.md)
+ - 更新lb节点keepalived使用单播发送vrrp报文,预期兼容公有云上自建LB(待测试)
+ - 废弃原 ansible hosts 中变量SERVICE_PROXY
+ - 更新haproxy负载均衡算法配置
+- 其他修复:
+ - fix 变更集群网络的脚本和[文档](https://github.com/gjmzj/kubeasz/blob/master/docs/op/change_k8s_network.md)
+ - fix 脚本99.clean.yml清理环境变量
+ - fix metrics-server允许的client cert问题
+ - fix #242: 添加CA有效期参数,设定CA有效期为15年(131400h) (#245)
+ - fix helm安装出现Error: transport is closing (#248)
+ - fix harbor点击tag界面出现\"发生未知错误,请稍后再试" (#250)
+ - fix 脚本99.clean.yml清理 services softlink (#253)
+ - fix kube-apiserver-v1.8 使用真实数量的 apiserver-count (#254)
+ - fix 清理ipvs产生的网络接口
diff --git a/docs/release-notes/kubeasz-0.3.0.md b/docs/release-notes/kubeasz-0.3.0.md
new file mode 100644
index 0000000..af4ba52
--- /dev/null
+++ b/docs/release-notes/kubeasz-0.3.0.md
@@ -0,0 +1,26 @@
+## kubeasz-0.3.0 发布说明
+
+CHANGELOG:
+- 组件更新:
+ - k8s: v1.11.2/v1.10.6/v1.9.10/v1.8.15
+ - calico: v3.1.3
+ - kube-router: v0.2.0-beta.9
+- 功能更新:
+ - **增加集群备份与恢复** 功能与[说明](https://github.com/gjmzj/kubeasz/blob/master/docs/op/cluster_restore.md)
+ - **增加cilium网络插件** ,文档待更新
+ - **增加cluster-storage角色** 与[文档说明](https://github.com/gjmzj/kubeasz/blob/master/docs/setup/08-cluster-storage.md)
+ - 增加阿里云NAS存储支持
+ - 增加集群个性化[配置说明](https://github.com/gjmzj/kubeasz/blob/master/docs/setup/config_guide.md)与生成脚本`tools/init_vars.yml`
+ - 支持deploy节点与ansible执行节点分离,为一份代码创建多个集群准备
+- 其他:
+ - 更新 jenkins and plugins (#258)
+ - 重写 nfs动态存储脚本与文档
+ - 优化 cluster-addon 安装脚本
+ - 增加 docker 配置文件
+ - 更新 offline images 0.3
+ - 增加 batch/v2alpha支持
+ - 移动 DNS yaml文件至 /opt/kube/kube-system
+ - fix 多主集群下change_k8s_network时vip丢失问题
+ - fix 禁止节点使用系统swap
+ - fix 解压后的harbor安装文件没有执行权限问题
+ - fix Ubuntu 18.04无法安装haproxy、keepalived问题
diff --git a/docs/release-notes/kubeasz-0.3.1.md b/docs/release-notes/kubeasz-0.3.1.md
new file mode 100644
index 0000000..0bd1154
--- /dev/null
+++ b/docs/release-notes/kubeasz-0.3.1.md
@@ -0,0 +1,30 @@
+## kubeasz-0.3.1 发布说明
+
+CHANGELOG:
+- 组件更新:
+ - k8s: v1.11.3, v1.10.7
+ - kube-router: v0.2.0
+ - dashboard: v1.10.0
+ - docker: 17.03.2-ce (选择k8s官方测试稳定的版本)
+- 集群安装:
+ - **增加集群时间同步服务chrony** [说明](https://github.com/gjmzj/kubeasz/blob/master/docs/guide/chrony.md)
+ - **取消 Node节点 Bootstrap机制**,安装流程更稳定,配置更精简
+ - 简化 ansible host 文件配置,移除etcd、harbor 相关变量
+ - 拆分 prepare 阶段的安装脚本,增加设置系统 ulimit
+ - 增加多lb节点(多于2节点)配置支持 (#286)
+ - 增加可选配置lb 节点负载转发ingress controller NodePort service的功能
+ - 自定义 kubelet docker 存储目录 (#305)
+ - 增加变量配置支持多网卡情况时安装 flannel calico
+- 文档更新:
+ - 更新 kubeasz 公有云安装文档 https://github.com/gjmzj/kubeasz/blob/master/docs/setup/kubeasz_on_public_cloud.md
+ - 更新 java war应用部署实践 https://github.com/gjmzj/kubeasz/blob/master/docs/practice/java_war_app.md
+ - 更新 cilium 文档,翻译官方 cilium 安全策略例子(deathstar/starwar)
+ - 更新 harbor kubedns README 文档
+ - 更新集群安装部分文档
+- 其他:
+ - 修复 calicoctl 配置,修复calico/node跑在LB 主节点时使用`vip`作为`bgp peer`地址问题
+ - 修复 jq安装错误,补充ipset和ipvsadm安装
+ - 修复清除单节点脚本 tools/clean_one_node.yml
+ - 修复消除离线镜像不存在时安装的错误提示信息
+ - 修复多节点(超过2节点时)lb 备节点 router_id重复问题
+ - 锁定jenkins镜像tag、升级插件版本以及锁定安全插件 (#315)
diff --git a/docs/release-notes/kubeasz-0.4.0.md b/docs/release-notes/kubeasz-0.4.0.md
new file mode 100644
index 0000000..95c2fe2
--- /dev/null
+++ b/docs/release-notes/kubeasz-0.4.0.md
@@ -0,0 +1,29 @@
+## kubeasz-0.4.0 发布说明
+
+CHANGELOG:
+- 组件更新:
+ - k8s: v1.12.1, v1.10.8, v1.9.11 [注意 v1.12.1 kubelet日志bug](https://github.com/kubernetes/kubernetes/issues/69503)
+ - docker: 18.06.1-ce (选择k8s官方测试稳定的版本)
+ - metrics-server: v0.3.1
+ - coredns: 1.2.2, kube-dns 1.14.13
+ - heapster v1.5.4
+ - traefik 1.7.2
+- 集群安装:
+ - **更新 kubelet使用 webhook方式认证/授权** ,提高集群安全性
+ - 调整安装步骤中 kubectl 命令的执行以兼容公有云部署
+ - 调整部分安装步骤以兼容`ansible`执行节点与`deploy`节点分离
+ - 更新节点的安全加固脚本[ansible-os-hardening 5.0.0](https://github.com/dev-sec/ansible-os-hardening)
+- 文档更新:
+ - 新增`elasticsearch`集群[部署实践](https://github.com/gjmzj/kubeasz/blob/master/docs/practice/es_cluster.md)
+ - 更新[kubeasz 公有云安装文档](https://github.com/gjmzj/kubeasz/blob/master/docs/setup/kubeasz_on_public_cloud.md)
+ - 调整集群安装步骤文档目录及修改使用英文文件名
+ - 修改部分脚本内部注释为英文
+- 其他:
+ - 升级 promethus chart 7.1.4,grafana chart 1.16.0
+ - 升级 jenkins 安全插件和 k8s 插件版本 (#325)
+ - 修复 新增 master 节点时报变量未定义错误
+ - 修复 ipvs 模式下网络组件偶尔连不上`kubernetes svc`的错误
+ - 修复 Ansible 2.7 环境下 yum/apt 安装多个软件包的 DEPRECATION WARNING (#334)
+ - 修复 chrony 与 ntp 共存冲突问题 (#341)
+ - 修复 CentOS 下使用 ipvs 模式需依赖 conntrack-tools 软件包
+ - 修复 tools/change_k8s_network.yml 脚本
diff --git a/docs/release-notes/kubeasz-0.5.0.md b/docs/release-notes/kubeasz-0.5.0.md
new file mode 100644
index 0000000..2ca19cd
--- /dev/null
+++ b/docs/release-notes/kubeasz-0.5.0.md
@@ -0,0 +1,29 @@
+## kubeasz-0.5.0 发布说明
+
+CHANGELOG:
+- 组件更新:
+ - k8s: v1.12.3, v1.11.5, v1.10.11
+ - calico v3.2.4
+ - helm v2.11.0
+ - traefik 1.7.4
+- 集群安装:
+ - 更新集群升级脚本和[文档](https://github.com/gjmzj/kubeasz/blob/master/docs/op/upgrade.md),关注[安全漏洞](https://mp.weixin.qq.com/s/Q8XngAr5RuL_irRscbVbKw)
+ - 集成 metallb 作为自有硬件 k8s 集群的 LoadBalancer 实现
+ - 支持[修改 APISERVER 证书](https://github.com/gjmzj/kubeasz/blob/master/docs/op/ch_apiserver_cert.md)
+ - 增加 ingress nodeport 负载转发的脚本与[文档](https://github.com/gjmzj/kubeasz/blob/master/docs/op/loadballance_ingress_nodeport.md)
+ - 增加 https ingress 配置和[文档](https://github.com/gjmzj/kubeasz/blob/master/docs/guide/ingress-tls.md)
+ - 增加 kubectl 只读访问权限配置和[文档](https://github.com/gjmzj/kubeasz/blob/master/docs/op/readonly_kubectl.md)
+ - 增加 apiserver 配置支持 istio sidecar自动注入webhook (#375)
+ - 初始化集群节点设置 net.netfilter.nf_conntrack_max=1000000
+ - 取消多主集群LB_IF参数设置,自动生成以避免人为配置疏忽
+- 文档更新:
+ - 更新[kubeasz 公有云安装文档](https://github.com/gjmzj/kubeasz/blob/master/docs/setup/kubeasz_on_public_cloud.md)
+ - 更新[metallb 文档](https://github.com/gjmzj/kubeasz/blob/master/docs/guide/metallb.md)
+ - 更新[dashboard 文档](https://github.com/gjmzj/kubeasz/blob/master/docs/guide/dashboard.md),支持只读权限设置
+ - 新增istio安装说明
+- 其他:
+ - 修复内核4.19加载nf_conntrack (#366)
+ - 修复 calico controller 中 NodePorts 的自动配置
+ - 取消 helms 别名设置
+ - 升级jenkins-lts版本和插件版本 (#358)
+ - 修复阿里云nas动态pv脚本
diff --git a/docs/release-notes/kubeasz-0.5.1.md b/docs/release-notes/kubeasz-0.5.1.md
new file mode 100644
index 0000000..928175c
--- /dev/null
+++ b/docs/release-notes/kubeasz-0.5.1.md
@@ -0,0 +1,24 @@
+## kubeasz-0.5.1 发布说明
+
+CHANGELOG:
+- 组件更新:
+ - k8s: v1.13.2, v1.12.4, v1.11.6, v1.10.12
+ - calico v3.3.2
+ - coredns 1.2.6
+- 集群安装:
+ - 更新 calico 3.3.2,并保留3.2.4可选
+ - 修复特定环境下lb节点变量LB_IF自动设置错误
+ - 移除 kube-node csr 请求批准部分(PR #399)
+ - 添加支持 RedHat (PR #431)
+ - 修改 docker 存储的目录设置(PR #436)
+ - 更新 kube-schedule 监听参数 (PR #440)
+ - 安装流程增加等待 ETCD 同步完成再返回成功(PR #420)
+ - 增加 pod-infra-container 可选择配置
+ - 增加 nginx-ingress manifests
+- 文档更新:
+ - **增加 [calico 设置route reflector文档](https://github.com/gjmzj/kubeasz/blob/master/docs/setup/network-plugin/calico-bgp-rr.md)**,大规模k8s集群使用calico网络必读
+ - 部分文档更新优化,部分文档中内部链接修复(PR #429)
+ - 增加 dashboard ingress [配置示例](https://github.com/gjmzj/kubeasz/blob/master/docs/guide/ingress-tls.md#%E9%85%8D%E7%BD%AE-dashboard-ingress)
+- 其他:
+ - 添加 helm tls 环境变量(PR #398)
+ - 修复 dashboard ingress 配置(issue #403)
diff --git a/docs/release-notes/kubeasz-0.6.0.md b/docs/release-notes/kubeasz-0.6.0.md
new file mode 100644
index 0000000..66e29b8
--- /dev/null
+++ b/docs/release-notes/kubeasz-0.6.0.md
@@ -0,0 +1,36 @@
+## kubeasz-0.6.0 发布说明
+
+- Note: 本次为 kubeasz-0.x 最后一次版本发布,它将被并入 release-0 分支,停止主要更新,仅做 bug 修复版本;后续 master 分支将开始 kubeasz-1.x 版本发布。
+- Action Required: 本次更新修改 ansible hosts 文件,如需要更新已有项目使用,请按照 example 目录中的对应例子修改`/etc/ansible/hosts`文件。
+
+CHANGELOG:
+- 组件更新:
+ - k8s: v1.13.3
+ - calico v3.4.1
+ - flannel v0.11.0-amd64
+ - docker 18.09.2
+ - harbor 1.6.3
+ - helm/tiller: v2.12.3
+- 集群安装:
+ - **增加添加/删除 etcd 节点**脚本和[文档](https://github.com/gjmzj/kubeasz/blob/master/docs/op/op-etcd.md)
+ - **增加可选配置附加负载均衡节点(ex-lb)**,可用于负载均衡 NodePort 方式暴露的服务
+ - 更新删除节点脚本和[文档](https://github.com/gjmzj/kubeasz/blob/master/docs/op/del_one_node.md)
+ - 优化增加 node 和增加 master 节点流程
+ - 更新 harbor 安装流程和文档
+ - 优化 prepare tasks,避免把证书和 kubeconfig 分发到不需要的节点
+ - 更新 prometheus 告警发送钉钉配置和[文档](https://github.com/gjmzj/kubeasz/blob/master/docs/guide/prometheus.md#%E5%8F%AF%E9%80%89-%E9%85%8D%E7%BD%AE%E9%92%89%E9%92%89%E5%91%8A%E8%AD%A6)
+ - 增加使用 helm 部署 mariadb 集群和文档
+ - 增加 k8s 官方 mysql 集群示意配置
+ - 增加使用 helm 部署 redis-ha 集群
+ - 增加开机启动 k8s 相关内核模块配置
+ - 更新 calico 3.4.1,并保留版本 3.3.x/3.2.x 可选
+- 文档更新:
+ - **增加 gitlab-ci 文档**, https://github.com/gjmzj/kubeasz/blob/master/docs/guide/gitlab/readme.md
+ - 部分文档更新(helm/dns/chrony)
+- 其他:
+ - 修复为兼容k8s版本 <= 1.11,revert PR #440
+ - 修复清除iptables规则时无法连接节点(PR #453 by PowerDos)
+ - 添加开启docker远程API选项(默认关闭)(PR #444 by lusyoe)
+ - 修复 calico 3.3.x rbac 配置(PR #447 by sunshanpeng)
+ - 增加 coredns 和 calico 的 metrics 监控选项(PR #447 by sunshanpeng)
+ - 添加 helm 离线安装方法说明(doc/guide/helm.md)(PR #443 by j4ckzh0u)
diff --git a/docs/release-notes/kubeasz-1.0.0rc1 b/docs/release-notes/kubeasz-1.0.0rc1
new file mode 100644
index 0000000..7a3ec4a
--- /dev/null
+++ b/docs/release-notes/kubeasz-1.0.0rc1
@@ -0,0 +1,26 @@
+## kubeasz-1.0.0rc1 发布说明
+
+- Note: kubeasz-1.x 第一个版本预发布,原 master 已并入 release-0 分支,停止主要更新,仅做 bug 修复版本;后续 master 分支将开始 kubeasz-1.x 版本发布。
+- Action Required: 本次更新修改 ansible hosts 文件,请按照 example 目录中的对应例子修改`/etc/ansible/hosts`文件, 确保 ansible hosts 文件中主机组的顺序与例子一致。
+
+CHANGELOG:
+- 组件更新:
+ - k8s: v1.13.4
+ - cilium v1.4.1
+- 集群安装:
+ - **引入[easzctl](https://github.com/gjmzj/kubeasz/blob/master/tools/easzctl)命令行工具**,后续它将作为推荐的集群常规管理工具,包括多集群管理(to do)
+ - **新增 docker 运行安装 kubeasz**,请参考文档 https://github.com/gjmzj/kubeasz/blob/master/docs/setup/docker_kubeasz.md
+ - 优化 example hosts 配置,废弃 new-node/new-master/new-etcd 主机组,废弃变量K8S_VER,改为自动识别
+ - 集成以下集群操作至 easzctl 命令行
+ - [添加 master](https://github.com/gjmzj/kubeasz/blob/master/docs/op/AddMaster.md)
+ - [添加 node](https://github.com/gjmzj/kubeasz/blob/master/docs/op/AddNode.md)
+ - [添加 etcd](https://github.com/gjmzj/kubeasz/blob/master/docs/op/op-etcd.md)
+ - [删除 etcd](https://github.com/gjmzj/kubeasz/blob/master/docs/op/op-etcd.md)
+ - [删除节点](https://github.com/gjmzj/kubeasz/blob/master/docs/op/clean_one_node.md)
+ - [快速创建 aio 集群]()
+ - 修改安装时生成随机 basic auth 密码
+ - 修改优化部分安装脚本以兼容 docker 运行 kubeasz
+ - update cilium v1.4.1,更新 cilium 文档(to do)
+ - 增加启动 kubeasz 容器的脚本 tools/kubeasz-docker
+- 其他:
+ - 修复兼容 docker 18.09.x 版本安装
diff --git a/docs/setup/00-planning_and_overall_intro.md b/docs/setup/00-planning_and_overall_intro.md
new file mode 100644
index 0000000..f986eec
--- /dev/null
+++ b/docs/setup/00-planning_and_overall_intro.md
@@ -0,0 +1,151 @@
+## 00-集群规划和基础参数设定
+
+多节点高可用集群部署步骤与[AllinOne部署](quickStart.md)基本一致,增加LB 负载均衡部署步骤。
+
+- 注意1:请确保各节点时区设置一致、时间同步。 如果你的环境没有提供NTP 时间同步,推荐集成安装[chrony](../guide/chrony.md)
+- 注意2:如果需要在公有云上创建多主多节点集群,请结合阅读[在公有云上部署 kubeasz](kubeasz_on_public_cloud.md)
+
+## 高可用集群所需节点配置如下
+
+|角色|数量|描述|
+|:-|:-|:-|
+|deploy节点|1|运行这份 ansible 脚本的节点|
+|etcd节点|3|注意etcd集群必须是1,3,5,7...奇数个节点|
+|master节点|2|需要额外规划一个master VIP(虚地址),可根据需要提升机器配置或增加节点数|
+|lb节点|2|负载均衡节点两个,安装 haproxy+keepalived|
+|node节点|3|运行应用负载的节点,可根据需要提升机器配置或增加节点数|
+
+项目预定义了4个例子,请修改后完成适合你的集群规划,生产环境建议一个节点只是一个角色。
+
++ [单节点](../../example/hosts.allinone.example)
++ [单主多节点](../../example/hosts.s-master.example)
++ [多主多节点](../../example/hosts.m-masters.example)
++ [在公有云上部署](../../example/hosts.cloud.example)
+
+## 部署步骤
+
+按照[多主多节点](../../example/hosts.m-masters.example)示例的节点配置,准备4台虚机,搭建一个多主高可用集群。
+
+### 1.基础系统配置
+
++ 推荐内存2G/硬盘30G以上
++ 最小化安装`Ubuntu 16.04 server`或者`CentOS 7 Minimal`
++ 配置基础网络、更新源、SSH登陆等
+
+### 2.在每个节点安装依赖工具
+
+Ubuntu 16.04 请执行以下脚本:
+
+``` bash
+# 文档中脚本默认均以root用户执行
+apt-get update && apt-get upgrade -y && apt-get dist-upgrade -y
+# 安装python2
+apt-get install python2.7
+# Ubuntu16.04可能需要配置以下软连接
+ln -s /usr/bin/python2.7 /usr/bin/python
+```
+CentOS 7 请执行以下脚本:
+
+``` bash
+# 文档中脚本默认均以root用户执行
+# 安装 epel 源并更新
+yum install epel-release -y
+yum update
+# 安装python
+yum install python -y
+```
+### 3.在deploy节点安装及准备ansible
+
+- pip 安装 ansible(如果 Ubuntu pip报错,请看[附录](00-planning_and_overall_intro.md#Appendix))
+
+``` bash
+# Ubuntu 16.04
+apt-get install git python-pip -y
+# CentOS 7
+yum install git python-pip -y
+# pip安装ansible(国内如果安装太慢可以直接用pip阿里云加速)
+#pip install pip --upgrade
+#pip install ansible
+pip install pip --upgrade -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
+pip install --no-cache-dir ansible -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
+```
+
+- 在deploy节点配置免密码登陆
+
+``` bash
+ssh-keygen -t rsa -b 2048 回车 回车 回车
+ssh-copy-id $IPs #$IPs为所有节点地址包括自身,按照提示输入yes 和root密码
+```
+### 4.在deploy节点编排k8s安装
+
+- 4.1 下载项目源码
+
+``` bash
+# 方式一:使用git clone
+git clone https://github.com/gjmzj/kubeasz.git
+mkdir -p /etc/ansible
+mv kubeasz/* /etc/ansible
+# 方式二:从发布页面 https://github.com/gjmzj/kubeasz/releases 下载源码解压到同样目录
+```
+- 4.2a 下载二进制文件
+请从分享的[百度云链接](https://pan.baidu.com/s/1c4RFaA),下载解压到/etc/ansible/bin目录,如果你有合适网络环境也可以按照/down/download.sh自行从官网下载各种tar包
+
+``` bash
+tar zxvf k8s.1-9-8.tar.gz # 以安装k8s v1.9.8为例
+mv bin/* /etc/ansible/bin
+```
+- 4.2b [可选]下载离线docker镜像
+服务器使用内部yum源/apt源,但是无法访问公网情况下,请下载离线docker镜像完成集群安装;从百度云盘把`basic_images_kubeasz_x.y.tar.gz` 下载解压到`/etc/ansible/down` 目录
+
+``` bash
+tar zxvf basic_images_kubeasz_0.2.tar.gz -C /etc/ansible/down
+```
+- 4.3 配置集群参数
+ - 4.3.1 必要配置:`cd /etc/ansible && cp example/hosts.m-masters.example hosts`, 然后实际情况修改此hosts文件
+ - 4.3.2 可选配置,初次使用可以不做修改,详见[配置指南](config_guide.md)
+ - 4.3.3 验证ansible 安装:`ansible all -m ping` 正常能看到节点返回 SUCCESS
+
+- 4.4 开始安装
+如果你对集群安装流程不熟悉,请阅读项目首页 **安装步骤** 讲解后分步安装,并对 **每步都进行验证**
+
+``` bash
+# 分步安装
+ansible-playbook 01.prepare.yml
+ansible-playbook 02.etcd.yml
+ansible-playbook 03.docker.yml
+ansible-playbook 04.kube-master.yml
+ansible-playbook 05.kube-node.yml
+ansible-playbook 06.network.yml
+ansible-playbook 07.cluster-addon.yml
+# 一步安装
+#ansible-playbook 90.setup.yml
+```
+
++ [可选]对集群所有节点进行操作系统层面的安全加固 `ansible-playbook roles/os-harden/os-harden.yml`,详情请参考[os-harden项目](https://github.com/dev-sec/ansible-os-hardening)
+
+## Appendix
+
+- Ubuntu 1604 安装 ansible 如果出现以下错误
+
+``` bash
+Traceback (most recent call last):
+ File "/usr/bin/pip", line 9, in
+ from pip import main
+ImportError: cannot import name main
+```
+将`/usr/bin/pip`做以下修改即可
+
+``` bash
+#原代码
+from pip import main
+if __name__ == '__main__':
+ sys.exit(main())
+
+#修改后
+from pip import __main__
+if __name__ == '__main__':
+ sys.exit(__main__._main())
+```
+
+
+[后一篇](01-CA_and_prerequisite.md)
diff --git a/docs/setup/01-CA_and_prerequisite.md b/docs/setup/01-CA_and_prerequisite.md
new file mode 100644
index 0000000..909da08
--- /dev/null
+++ b/docs/setup/01-CA_and_prerequisite.md
@@ -0,0 +1,363 @@
+# 01-创建证书和环境配置
+
+本步骤[01.prepare.yml](../../01.prepare.yml)主要完成:
+
+- chrony role: 集群节点时间同步[可选]
+- deploy role: 创建CA证书、kubeconfig、kube-proxy.kubeconfig
+- prepare role: 分发CA证书、kubectl客户端安装、环境配置
+- lb role: 安装负载均衡[可选]
+
+## deploy 角色
+
+请在另外窗口打开[roles/deploy/tasks/main.yml](../../roles/deploy/tasks/main.yml) 文件,对照看以下讲解内容。
+
+### 创建 CA 证书和秘钥
+``` bash
+roles/deploy/
+├── tasks
+│ └── main.yml
+└── templates
+ ├── admin-csr.json.j2 # kubectl客户端使用的证书请求模板
+ ├── ca-config.json.j2 # ca 配置文件模板
+ ├── ca-csr.json.j2 # ca 证书签名请求模板
+ ├── kubedns.yaml.j2
+ └── kube-proxy-csr.json.j2 # kube-proxy使用的证书请求模板
+```
+kubernetes 系统各组件需要使用 TLS 证书对通信进行加密,使用 CloudFlare 的 PKI 工具集生成自签名的 CA 证书,用来签名后续创建的其它 TLS 证书。[参考阅读](https://coreos.com/os/docs/latest/generate-self-signed-certificates.html)
+
+根据认证对象可以将证书分成三类:服务器证书`server cert`,客户端证书`client cert`,对等证书`peer cert`(表示既是`server cert`又是`client cert`),在kubernetes 集群中需要的证书种类如下:
+
++ `etcd` 节点需要标识自己服务的`server cert`,也需要`client cert`与`etcd`集群其他节点交互,当然可以分别指定2个证书,也可以使用一个对等证书
++ `master` 节点需要标识 apiserver服务的`server cert`,也需要`client cert`连接`etcd`集群,这里也使用一个对等证书
++ `kubectl` `calico` `kube-proxy` 只需要`client cert`,因此证书请求中 `hosts` 字段可以为空
++ `kubelet` 证书比较特殊,不是手动生成,它由node节点`TLS BootStrap` 向`apiserver`请求,由`master`节点的`controller-manager` 自动签发,包含一个`client cert` 和一个`server cert`
+
+整个集群要使用统一的CA 证书,只需要在 deploy 节点创建,然后分发给其他节点;为了保证安装的幂等性,如果已经存在CA 证书,就跳过创建CA 步骤
+
+#### 创建 CA 配置文件 [ca-config.json.j2](../../roles/deploy/templates/ca-config.json.j2)
+``` bash
+{
+ "signing": {
+ "default": {
+ "expiry": "87600h"
+ },
+ "profiles": {
+ "kubernetes": {
+ "usages": [
+ "signing",
+ "key encipherment",
+ "server auth",
+ "client auth"
+ ],
+ "expiry": "87600h"
+ }
+ }
+ }
+}
+```
++ `signing`:表示该证书可用于签名其它证书;生成的 ca.pem 证书中 `CA=TRUE`;
++ `server auth`:表示可以用该 CA 对 server 提供的证书进行验证;
++ `client auth`:表示可以用该 CA 对 client 提供的证书进行验证;
++ `profile kubernetes` 包含了`server auth`和`client auth`,所以可以签发三种不同类型证书;
+
+#### 创建 CA 证书签名请求 [ca-csr.json.j2](../../roles/deploy/templates/ca-csr.json.j2)
+``` bash
+{
+ "CN": "kubernetes",
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ],
+ "ca": {
+ "expiry": "876000h"
+ }
+}
+```
+
+#### 生成CA 证书和私钥
+``` bash
+cfssl gencert -initca ca-csr.json | cfssljson -bare ca
+```
+
+### 生成 kubeconfig 配置文件
+
+kubectl使用~/.kube/config 配置文件与kube-apiserver进行交互,且拥有管理 K8S集群的完全权限,
+
+准备kubectl使用的admin 证书签名请求 [admin-csr.json.j2](../../roles/deploy/templates/admin-csr.json.j2)
+
+``` bash
+{
+ "CN": "admin",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "system:masters",
+ "OU": "System"
+ }
+ ]
+}
+
+```
++ kubectl 使用客户端证书可以不指定hosts 字段
++ 证书请求中 `O` 指定该证书的 Group 为 `system:masters`,而 `RBAC` 预定义的 `ClusterRoleBinding` 将 Group `system:masters` 与 ClusterRole `cluster-admin` 绑定,这就赋予了kubectl**所有集群权限**
+
+``` bash
+$ kubectl describe clusterrolebinding cluster-admin
+Name: cluster-admin
+Labels: kubernetes.io/bootstrapping=rbac-defaults
+Annotations: rbac.authorization.kubernetes.io/autoupdate=true
+Role:
+ Kind: ClusterRole
+ Name: cluster-admin
+Subjects:
+ Kind Name Namespace
+ ---- ---- ---------
+ Group system:masters
+```
+
+#### 生成 cluster-admin 用户证书
+
+```
+cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin
+```
+
+#### 生成 ~/.kube/config 配置文件
+
+使用`kubectl config` 生成kubeconfig 自动保存到 ~/.kube/config,生成后 `cat ~/.kube/config`可以验证配置文件包含 kube-apiserver 地址、证书、用户名等信息。
+
+```
+kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=127.0.0.1:8443
+kubectl config set-credentials admin --client-certificate=admin.pem --embed-certs=true --client-key=admin-key.pem
+kubectl config set-context kubernetes --cluster=kubernetes --user=admin
+kubectl config use-context kubernetes
+```
+
+### 生成 kube-proxy.kubeconfig 配置文件
+
+创建 kube-proxy 证书请求
+
+``` bash
+{
+ "CN": "system:kube-proxy",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
+```
++ kube-proxy 使用客户端证书可以不指定hosts 字段
++ CN 指定该证书的 User 为 system:kube-proxy,预定义的 ClusterRoleBinding system:node-proxier 将User system:kube-proxy 与 Role system:node-proxier 绑定,授予了调用 kube-apiserver Proxy 相关 API 的权限;
+
+``` bash
+$ kubectl describe clusterrolebinding system:node-proxier
+Name: system:node-proxier
+Labels: kubernetes.io/bootstrapping=rbac-defaults
+Annotations: rbac.authorization.kubernetes.io/autoupdate=true
+Role:
+ Kind: ClusterRole
+ Name: system:node-proxier
+Subjects:
+ Kind Name Namespace
+ ---- ---- ---------
+ User system:kube-proxy
+```
+
+#### 生成 system:kube-proxy 用户证书
+
+```
+cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy
+```
+
+#### 生成 kube-proxy.kubeconfig
+
+使用`kubectl config` 生成kubeconfig 自动保存到 kube-proxy.kubeconfig
+
+```
+kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=127.0.0.1:8443 --kubeconfig=kube-proxy.kubeconfig
+kubectl config set-credentials kube-proxy --client-certificate=kube-proxy.pem --embed-certs=true --client-key=kube-proxy-key.pem --kubeconfig=kube-proxy.kubeconfig
+kubectl config set-context default --cluster=kubernetes --user=kube-proxy --kubeconfig=kube-proxy.kubeconfig
+kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
+```
+
+## prepare 角色
+
+``` bash
+roles/prepare/
+├── files
+│ ├── 95-k8s-sysctl.conf
+└── tasks
+ └── main.yml
+```
+请在另外窗口打开[roles/prepare/tasks/main.yml](../../roles/prepare/tasks/main.yml) 文件,比较简单直观
+
+1. 首先创建一些基础文件目录
+1. 修改环境变量,把{{ bin_dir }} 添加到$PATH,需要重新登陆 shell生效
+1. 把证书工具 CFSSL 和 kubectl 下发到指定节点,并下发kubeconfig配置文件
+1. 把CA 证书相关下发到指定节点的 {{ ca_dir }} 目录
+1. 最后设置基础操作系统软件和系统参数,请阅读脚本中的注释内容
+
+## LB 角色-负载均衡部署
+``` bash
+roles/lb
+├── tasks
+│ └── main.yml
+└── templates
+ ├── haproxy.cfg.j2
+ ├── haproxy.service.j2
+ ├── keepalived-backup.conf.j2
+ └── keepalived-master.conf.j2
+```
+
+Haproxy支持四层和七层负载,稳定性好,根据官方文档,HAProxy可以跑满10Gbps-New benchmark of HAProxy at 10 Gbps using Myricom's 10GbE NICs (Myri-10G PCI-Express);另外,openstack高可用也有用haproxy的。
+
+keepalived观其名可知,保持存活,它是基于VRRP协议保证所谓的高可用或热备的,这里用来预防haproxy的单点故障。
+
+keepalived与haproxy配合,实现master的高可用过程如下:
+
++ 1.keepalived利用vrrp协议生成一个虚拟地址(VIP),正常情况下VIP存活在keepalive的主节点,当主节点故障时,VIP能够漂移到keepalived的备节点,保障VIP地址可用性。
++ 2.在keepalived的主备节点都配置相同haproxy负载配置,并且监听客户端请求在VIP的地址上,保障随时都有一个haproxy负载均衡在正常工作。并且keepalived启用对haproxy进程的存活检测,一旦主节点haproxy进程故障,VIP也能切换到备节点,从而让备节点的haproxy进行负载工作。
++ 3.在haproxy的配置中配置多个后端真实kube-apiserver的endpoints,并启用存活监测后端kube-apiserver,如果一个kube-apiserver故障,haproxy会将其剔除负载池。
+
+请在另外窗口打开[roles/lb/tasks/main.yml](../../roles/lb/tasks/main.yml) 文件,对照看以下讲解内容。
+
+#### 安装haproxy
+
++ 使用apt源安装
+
+#### 配置haproxy [haproxy.cfg.j2](../../roles/lb/templates/haproxy.cfg.j2)
+``` bash
+global
+ log /dev/log local0
+ log /dev/log local1 notice
+ chroot /var/lib/haproxy
+ stats socket /run/haproxy/admin.sock mode 660 level admin
+ stats timeout 30s
+ user haproxy
+ group haproxy
+ daemon
+ nbproc 1
+
+defaults
+ log global
+ timeout connect 5000
+ timeout client 50000
+ timeout server 50000
+
+listen kube-master
+ bind 0.0.0.0:{{ KUBE_APISERVER.split(':')[2] }}
+ mode tcp
+ option tcplog
+ balance source
+ server s1 {{ master1 }} check inter 10000 fall 2 rise 2 weight 1
+ server s2 {{ master2 }} check inter 10000 fall 2 rise 2 weight 1
+```
+如果用apt安装的话,可以在/usr/share/doc/haproxy目录下找到配置指南configuration.txt.gz,全局和默认配置这里不展开,关注`listen` 代理设置模块,各项配置说明:
++ 名称 kube-master
++ bind 监听客户端请求的地址/端口,保证监听master的VIP地址和端口
++ mode 选择四层负载模式 (当然你也可以选择七层负载,请查阅指南,适当调整)
++ balance 选择负载算法 (负载算法也有很多供选择)
++ server 配置master节点真实的endpoits,必须与 [hosts文件](../../example/hosts.m-masters.example)对应设置
+
+#### 安装keepalived
+
++ 使用apt源安装
+
+#### 配置keepalived主节点 [keepalived-master.conf.j2](../../roles/lb/templates/keepalived-master.conf.j2)
+``` bash
+global_defs {
+ router_id lb-master
+}
+
+vrrp_script check-haproxy {
+ script "killall -0 haproxy"
+ interval 5
+ weight -30
+}
+
+vrrp_instance VI-kube-master {
+ state MASTER
+ priority 120
+ dont_track_primary
+ interface {{ LB_IF }}
+ virtual_router_id {{ ROUTER_ID }}
+ advert_int 3
+ track_script {
+ check-haproxy
+ }
+ virtual_ipaddress {
+ {{ MASTER_IP }}
+ }
+}
+```
++ vrrp_script 定义了监测haproxy进程的脚本,利用shell 脚本`killall -0 haproxy` 进行检测进程是否存活,如果进程不存在,根据`weight -30`设置将主节点优先级降低30,这样原先备节点将变成主节点。
++ vrrp_instance 定义了vrrp组,包括优先级、使用端口、router_id、心跳频率、检测脚本、虚拟地址VIP等
++ 特别注意 `virtual_router_id` 标识了一个 VRRP组,在同网段下必须唯一,否则出现 `Keepalived_vrrp: bogus VRRP packet received on eth0 !!!`类似报错
+
+#### 配置keepalived备节点 [keepalived-backup.conf.j2](../../roles/lb/templates/keepalived-backup.conf.j2)
+``` bash
+global_defs {
+ router_id lb-backup
+}
+
+vrrp_instance VI-kube-master {
+ state BACKUP
+ priority 110
+ dont_track_primary
+ interface {{ LB_IF }}
+ virtual_router_id {{ ROUTER_ID }}
+ advert_int 3
+ virtual_ipaddress {
+ {{ MASTER_IP }}
+ }
+}
+```
++ 备节点的配置类似主节点,除了优先级和检测脚本,其他如 `virtual_router_id` `advert_int` `virtual_ipaddress`必须与主节点一致
+
+### 启动 keepalived 和 haproxy 后验证
+
++ lb 节点验证
+
+``` bash
+systemctl status haproxy # 检查进程状态
+journalctl -u haproxy # 检查进程日志是否有报错信息
+systemctl status keepalived # 检查进程状态
+journalctl -u keepalived # 检查进程日志是否有报错信息
+netstat -antlp|grep 8443 # 检查tcp端口是否监听
+```
++ 在 keepalived 主节点
+
+``` bash
+ip a # 检查 master的 VIP地址是否存在
+```
+### keepalived 主备切换演练
+
+1. 尝试关闭 keepalived主节点上的 haproxy进程,然后在keepalived 备节点上查看 master的 VIP地址是否能够漂移过来,并依次检查上一步中的验证项。
+1. 尝试直接关闭 keepalived 主节点系统,检查各验证项。
+
+[后一篇](02-install_etcd.md)
diff --git a/docs/setup/02-install_etcd.md b/docs/setup/02-install_etcd.md
new file mode 100644
index 0000000..23d5ee0
--- /dev/null
+++ b/docs/setup/02-install_etcd.md
@@ -0,0 +1,124 @@
+## 02-安装etcd集群
+
+kuberntes 系统使用 etcd 存储所有数据,是最重要的组件之一,注意 etcd集群只能有奇数个节点(1,3,5...),本文档使用3个节点做集群。
+
+请在另外窗口打开[roles/etcd/tasks/main.yml](../../roles/etcd/tasks/main.yml) 文件,对照看以下讲解内容。
+
+### 下载etcd/etcdctl 二进制文件、创建证书目录
+
+https://github.com/etcd-io/etcd/releases
+
+### 创建etcd证书请求 [etcd-csr.json.j2](../../roles/etcd/templates/etcd-csr.json.j2)
+
+首先判断下是否etcd 证书已经存在,如果已经存在就跳过证书生成步骤
+
+``` bash
+{
+ "CN": "etcd",
+ "hosts": [
+ "127.0.0.1",
+ "{{ inventory_hostname }}"
+ ],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
+```
++ etcd使用对等证书,hosts 字段必须指定授权使用该证书的 etcd 节点 IP
+
+### 创建证书和私钥
+
+``` bash
+cd /etc/etcd/ssl && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes etcd-csr.json | {{ bin_dir }}/cfssljson -bare etcd
+```
+
+### 创建etcd 服务文件 [etcd.service.j2](../../roles/etcd/templates/etcd.service.j2)
+
+先创建工作目录 /var/lib/etcd/
+
+``` bash
+[Unit]
+Description=Etcd Server
+After=network.target
+After=network-online.target
+Wants=network-online.target
+Documentation=https://github.com/coreos
+
+[Service]
+Type=notify
+WorkingDirectory=/var/lib/etcd/
+ExecStart={{ bin_dir }}/etcd \
+ --name={{ NODE_NAME }} \
+ --cert-file=/etc/etcd/ssl/etcd.pem \
+ --key-file=/etc/etcd/ssl/etcd-key.pem \
+ --peer-cert-file=/etc/etcd/ssl/etcd.pem \
+ --peer-key-file=/etc/etcd/ssl/etcd-key.pem \
+ --trusted-ca-file={{ ca_dir }}/ca.pem \
+ --peer-trusted-ca-file={{ ca_dir }}/ca.pem \
+ --initial-advertise-peer-urls=https://{{ inventory_hostname }}:2380 \
+ --listen-peer-urls=https://{{ inventory_hostname }}:2380 \
+ --listen-client-urls=https://{{ inventory_hostname }}:2379,http://127.0.0.1:2379 \
+ --advertise-client-urls=https://{{ inventory_hostname }}:2379 \
+ --initial-cluster-token=etcd-cluster-0 \
+ --initial-cluster={{ ETCD_NODES }} \
+ --initial-cluster-state=new \
+ --data-dir=/var/lib/etcd
+Restart=on-failure
+RestartSec=5
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
+```
++ 完整参数列表请使用 `etcd --help` 查询
++ 注意etcd 即需要服务器证书也需要客户端证书,这里为方便使用一个peer 证书代替两个证书,更多证书相关请阅读 [01-创建CA证书和环境配置](01-CA_and_prerequisite.md)
++ 注意{{ }} 中的参数与ansible hosts文件中设置对应
++ `--initial-cluster-state` 值为 `new` 时,`--name` 的参数值必须位于 `--initial-cluster` 列表中;
+
+### 启动etcd服务
+
+``` bash
+systemctl daemon-reload && systemctl enable etcd && systemctl start etcd
+```
+
+### 验证etcd集群状态
+
++ systemctl status etcd 查看服务状态
++ journalctl -u etcd 查看运行日志
++ 在任一 etcd 集群节点上执行如下命令
+
+``` bash
+# 根据hosts中配置设置shell变量 $NODE_IPS
+export NODE_IPS="192.168.1.1 192.168.1.2 192.168.1.3"
+for ip in ${NODE_IPS}; do
+ ETCDCTL_API=3 etcdctl \
+ --endpoints=https://${ip}:2379 \
+ --cacert=/etc/kubernetes/ssl/ca.pem \
+ --cert=/etc/etcd/ssl/etcd.pem \
+ --key=/etc/etcd/ssl/etcd-key.pem \
+ endpoint health; done
+```
+预期结果:
+
+``` text
+https://192.168.1.1:2379 is healthy: successfully committed proposal: took = 2.210885ms
+https://192.168.1.2:2379 is healthy: successfully committed proposal: took = 2.784043ms
+https://192.168.1.3:2379 is healthy: successfully committed proposal: took = 3.275709ms
+```
+三台 etcd 的输出均为 healthy 时表示集群服务正常。
+
+[后一篇](03-install_docker.md)
diff --git a/docs/setup/03-install_docker.md b/docs/setup/03-install_docker.md
new file mode 100644
index 0000000..9d1dd8c
--- /dev/null
+++ b/docs/setup/03-install_docker.md
@@ -0,0 +1,159 @@
+## 03-安装docker服务
+
+``` bash
+roles/docker/
+├── files
+│ ├── daemon.json
+│ ├── docker
+│ └── docker-tag
+├── tasks
+│ └── main.yml
+└── templates
+ └── docker.service.j2
+```
+
+请在另外窗口打开[roles/docker/tasks/main.yml](../../roles/docker/tasks/main.yml) 文件,对照看以下讲解内容。
+
+### 创建docker的systemd unit文件
+
+``` bash
+[Unit]
+Description=Docker Application Container Engine
+Documentation=http://docs.docker.io
+
+[Service]
+Environment="PATH={{ bin_dir }}:/bin:/sbin:/usr/bin:/usr/sbin"
+ExecStart={{ bin_dir }}/dockerd
+ExecStartPost=/sbin/iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT
+ExecReload=/bin/kill -s HUP $MAINPID
+Restart=on-failure
+RestartSec=5
+LimitNOFILE=infinity
+LimitNPROC=infinity
+LimitCORE=infinity
+Delegate=yes
+KillMode=process
+
+[Install]
+WantedBy=multi-user.target
+```
++ dockerd 运行时会调用其它 docker 命令,如 docker-proxy,所以需要将 docker 命令所在的目录加到 PATH 环境变量中;
++ docker 从 1.13 版本开始,将`iptables` 的`filter` 表的`FORWARD` 链的默认策略设置为`DROP`,从而导致 ping 其它 Node 上的 Pod IP 失败,因此必须在 `filter` 表的`FORWARD` 链增加一条默认允许规则 `iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT`
++ 运行`dockerd --help` 查看所有可配置参数,确保默认开启 `--iptables` 和 `--ip-masq` 选项
+
+### 配置国内镜像加速
+
+从国内下载docker官方仓库镜像非常缓慢,所以对于k8s集群来说配置镜像加速非常重要,配置 `/etc/docker/daemon.json`
+
+``` bash
+{
+ "registry-mirrors": ["https://registry.docker-cn.com"],
+ "max-concurrent-downloads": 10,
+ "log-driver": "json-file",
+ "log-level": "warn",
+ "log-opts": {
+ "max-size": "10m",
+ "max-file": "3"
+ }
+}
+```
+
+这将在后续部署calico下载 calico/node镜像和kubedns/heapster/dashboard镜像时起到重要加速效果。
+
+由于K8S的官方镜像存放在`gcr.io`仓库,因此这个镜像加速对K8S的官方镜像没有效果;好在`Docker Hub`上有很多K8S镜像的转存,而`Docker Hub`上的镜像可以加速。这里推荐两个K8S镜像的`Docker Hub`项目,几乎能找到所有K8S相关的镜像,而且更新及时,感谢维护者的辛勤付出!
+
++ [mirrorgooglecontainers](https://hub.docker.com/u/mirrorgooglecontainers/)
++ [anjia0532](https://hub.docker.com/u/anjia0532/), [项目github地址](https://github.com/anjia0532/gcr.io_mirror)
+
+当然对于企业内部应用的docker镜像,想要在K8S平台运行的话,特别是结合开发`CI/CD` 流程,肯定是需要部署私有镜像仓库的,后续会简单提到 `Harbor`的部署。
+
+另外,daemon.json配置中也配置了docker 容器日志相关参数,设置单个容器日志超过10M则进行回卷,回卷的副本数超过3个就进行清理。
+
+### 清理 iptables
+
+因为后续`calico`网络、`kube-proxy`等将大量使用 iptables规则,安装前清空所有`iptables`策略规则;常见发行版`Ubuntu`的 `ufw` 和 `CentOS`的 `firewalld`等基于`iptables`的防火墙最好直接卸载,避免不必要的冲突。
+
+``` bash
+iptables -F && iptables -X \
+ && iptables -F -t nat && iptables -X -t nat \
+ && iptables -F -t raw && iptables -X -t raw \
+ && iptables -F -t mangle && iptables -X -t mangle
+```
++ calico 网络支持 `network-policy`,使用的`calico-kube-controllers` 会使用到`iptables` 所有的四个表 `filter` `nat` `raw` `mangle`,所以一并清理
+
+### 启动 docker
+
+``` bash
+systemctl daemon-reload && systemctl enable docker && systemctl start docker
+```
+
+### 可选-安装docker查询镜像 tag的小工具
+
+docker官方目前没有提供在命令行直接查询某个镜像的tag信息的方式,网上找来一个脚本工具,使用很方便。
+
+``` bash
+$ docker-tag library/ubuntu
+"14.04"
+"16.04"
+"17.04"
+"latest"
+"trusty"
+"trusty-20171117"
+"xenial"
+"xenial-20171114"
+"zesty"
+"zesty-20171114"
+$ docker-tag mirrorgooglecontainers/kubernetes-dashboard-amd64
+"v0.1.0"
+"v1.0.0"
+"v1.0.0-beta1"
+"v1.0.1"
+"v1.1.0-beta1"
+"v1.1.0-beta2"
+"v1.1.0-beta3"
+"v1.7.0"
+"v1.7.1"
+"v1.8.0"
+```
++ 需要先apt安装轻量JSON处理程序 `jq`
++ 然后下载脚本即可使用
++ 脚本很简单,就一行命令如下
+
+``` bash
+#!/bin/bash
+curl -s -S "https://registry.hub.docker.com/v2/repositories/$@/tags/" | jq '."results"[]["name"]' |sort
+```
++ 对于 CentOS7 安装 `jq` 稍微费力一点,需要启用 `EPEL` 源
+
+``` bash
+wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
+rpm -ivh epel-release-latest-7.noarch.rpm
+yum install jq
+```
+
+### 验证
+
+运行`ansible-playbook 03.docker.yml` 成功后可以验证
+
+``` bash
+systemctl status docker # 服务状态
+journalctl -u docker # 运行日志
+docker version
+docker info
+```
+`iptables-save|grep FORWARD` 查看 iptables filter表 FORWARD链,最后要有一个 `-A FORWARD -j ACCEPT` 保底允许规则
+
+``` bash
+iptables-save|grep FORWARD
+:FORWARD ACCEPT [0:0]
+:FORWARD DROP [0:0]
+-A FORWARD -j DOCKER-USER
+-A FORWARD -j DOCKER-ISOLATION
+-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+-A FORWARD -o docker0 -j DOCKER
+-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
+-A FORWARD -i docker0 -o docker0 -j ACCEPT
+-A FORWARD -j ACCEPT
+```
+
+[后一篇](04-install_kube_master.md)
diff --git a/docs/setup/04-install_kube_master.md b/docs/setup/04-install_kube_master.md
new file mode 100644
index 0000000..fbc72b8
--- /dev/null
+++ b/docs/setup/04-install_kube_master.md
@@ -0,0 +1,242 @@
+## 04-安装kube-master节点
+
+部署master节点主要包含三个组件`apiserver` `scheduler` `controller-manager`,其中:
+
+- apiserver提供集群管理的REST API接口,包括认证授权、数据校验以及集群状态变更等
+ - 只有API Server才直接操作etcd
+ - 其他模块通过API Server查询或修改数据
+ - 提供其他模块之间的数据交互和通信的枢纽
+- scheduler负责分配调度Pod到集群内的node节点
+ - 监听kube-apiserver,查询还未分配Node的Pod
+ - 根据调度策略为这些Pod分配节点
+- controller-manager由一系列的控制器组成,它通过apiserver监控整个集群的状态,并确保集群处于预期的工作状态
+
+master节点的高可用主要就是实现apiserver组件的高可用,在之前部署lb节点时候已经配置haproxy对它进行负载均衡。
+
+``` text
+roles/kube-master/
+├── tasks
+│ └── main.yml
+└── templates
+ ├── basic-auth.csv.j2
+ ├── kube-apiserver.service.j2
+ ├── kube-controller-manager.service.j2
+ ├── kubernetes-csr.json.j2
+ ├── kube-scheduler.service.j2
+ └── token.csv.j2
+```
+
+请在另外窗口打开[roles/kube-master/tasks/main.yml](../../roles/kube-master/tasks/main.yml) 文件,对照看以下讲解内容。
+
+### 创建 kubernetes 证书签名请求
+
+增加判断是否已经有kubernetes证书,如果是就使用原证书,跳过生成证书步骤
+
+``` bash
+{
+ "CN": "kubernetes",
+ "hosts": [
+ "127.0.0.1",
+ "{{ MASTER_IP }}",
+ "{{ inventory_hostname }}",
+ "{{ CLUSTER_KUBERNETES_SVC_IP }}",
+ "kubernetes",
+ "kubernetes.default",
+ "kubernetes.default.svc",
+ "kubernetes.default.svc.cluster",
+ "kubernetes.default.svc.cluster.local"
+ ],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
+```
+- kubernetes 证书既是服务器证书,同时apiserver又作为客户端证书去访问etcd 集群;作为服务器证书需要设置hosts 指定使用该证书的IP 或域名列表,需要注意的是:
+ - 多主高可用集群需要把master VIP地址 {{ MASTER_IP }} 也添加进去
+ - `kubectl get svc` 将看到集群中由api-server 创建的默认服务 `kubernetes`,因此也要把 `kubernetes` 服务名和各个服务域名也添加进去
+- 注意所有{{ }}变量与ansible hosts中设置的对应关系
+
+### 创建基础用户名/密码认证配置
+
+可选,为后续使用基础认证的场景做准备,如实现dashboard 用不同用户名登陆绑定不同的权限,后续更新dashboard的实践文档。
+
+### 创建apiserver的服务配置文件
+
+``` bash
+[Unit]
+Description=Kubernetes API Server
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+After=network.target
+
+[Service]
+ExecStart={{ bin_dir }}/kube-apiserver \
+ --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook \
+ --bind-address={{ inventory_hostname }} \
+ --insecure-bind-address=127.0.0.1 \
+ --authorization-mode=Node,RBAC \
+ --kubelet-https=true \
+ --kubelet-client-certificate={{ ca_dir }}/admin.pem \
+ --kubelet-client-key={{ ca_dir }}/admin-key.pem \
+ --anonymous-auth=false \
+ --basic-auth-file={{ ca_dir }}/basic-auth.csv \
+ --service-cluster-ip-range={{ SERVICE_CIDR }} \
+ --service-node-port-range={{ NODE_PORT_RANGE }} \
+ --tls-cert-file={{ ca_dir }}/kubernetes.pem \
+ --tls-private-key-file={{ ca_dir }}/kubernetes-key.pem \
+ --client-ca-file={{ ca_dir }}/ca.pem \
+ --service-account-key-file={{ ca_dir }}/ca-key.pem \
+ --etcd-cafile={{ ca_dir }}/ca.pem \
+ --etcd-certfile={{ ca_dir }}/kubernetes.pem \
+ --etcd-keyfile={{ ca_dir }}/kubernetes-key.pem \
+ --etcd-servers={{ ETCD_ENDPOINTS }} \
+ --enable-swagger-ui=true \
+ --endpoint-reconciler-type=lease \
+ --allow-privileged=true \
+ --audit-log-maxage=30 \
+ --audit-log-maxbackup=3 \
+ --audit-log-maxsize=100 \
+ --audit-log-path=/var/lib/audit.log \
+ --event-ttl=1h \
+ --requestheader-client-ca-file={{ ca_dir }}/ca.pem \
+ --requestheader-allowed-names= \
+ --requestheader-extra-headers-prefix=X-Remote-Extra- \
+ --requestheader-group-headers=X-Remote-Group \
+ --requestheader-username-headers=X-Remote-User \
+ --proxy-client-cert-file={{ ca_dir }}/aggregator-proxy.pem \
+ --proxy-client-key-file={{ ca_dir }}/aggregator-proxy-key.pem \
+ --enable-aggregator-routing=true \
+ --runtime-config=batch/v2alpha1=true \
+ --v=2
+Restart=on-failure
+RestartSec=5
+Type=notify
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
+```
++ Kubernetes 对 API 访问需要依次经过认证、授权和准入控制(admission controll),认证解决用户是谁的问题,授权解决用户能做什么的问题,Admission Control则是资源管理方面的作用。
++ 支持同时提供https(默认监听在6443端口)和http API(默认监听在127.0.0.1的8080端口),其中http API是非安全接口,不做任何认证授权机制,kube-scheduler、kube-controller-manager 一般和 kube-apiserver 部署在同一台机器上,它们使用非安全端口和 kube-apiserver通信; 其他集群外部就使用HTTPS访问 apiserver
++ 关于authorization-mode=Node,RBAC v1.7+支持Node授权,配合NodeRestriction准入控制来限制kubelet仅可访问node、endpoint、pod、service以及secret、configmap、PV和PVC等相关的资源;需要注意的是v1.7中Node 授权是默认开启的,v1.8中需要显式配置开启,否则 Node无法正常工作
++ 缺省情况下 kubernetes 对象保存在 etcd /registry 路径下,可以通过 --etcd-prefix 参数进行调整
++ 详细参数配置请参考`kube-apiserver --help`,关于认证、授权和准入控制请[阅读](https://github.com/feiskyer/kubernetes-handbook/blob/master/components/apiserver.md)
++ 增加了访问kubelet使用的证书配置,防止匿名访问kubelet的安全漏洞,详见[漏洞说明](../mixes/01.fix_kubelet_annoymous_access.md)
+
+### 创建controller-manager 的服务文件
+
+``` bash
+[Unit]
+Description=Kubernetes Controller Manager
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+
+[Service]
+ExecStart={{ bin_dir }}/kube-controller-manager \
+ --address=127.0.0.1 \
+ --master=http://127.0.0.1:8080 \
+ --allocate-node-cidrs=true \
+ --service-cluster-ip-range={{ SERVICE_CIDR }} \
+ --cluster-cidr={{ CLUSTER_CIDR }} \
+ --cluster-name=kubernetes \
+ --cluster-signing-cert-file={{ ca_dir }}/ca.pem \
+ --cluster-signing-key-file={{ ca_dir }}/ca-key.pem \
+ --service-account-private-key-file={{ ca_dir }}/ca-key.pem \
+ --root-ca-file={{ ca_dir }}/ca.pem \
+ --horizontal-pod-autoscaler-use-rest-clients=true \
+ --leader-elect=true \
+ --v=2
+Restart=on-failure
+RestartSec=5
+
+[Install]
+WantedBy=multi-user.target
+```
++ --address 值必须为 127.0.0.1,因为当前 kube-apiserver 期望 scheduler 和 controller-manager 在同一台机器
++ --master=http://127.0.0.1:8080 使用非安全 8080 端口与 kube-apiserver 通信
++ --cluster-cidr 指定 Cluster 中 Pod 的 CIDR 范围,该网段在各 Node 间必须路由可达(calico 实现)
++ --service-cluster-ip-range 参数指定 Cluster 中 Service 的CIDR范围,必须和 kube-apiserver 中的参数一致
++ --cluster-signing-* 指定的证书和私钥文件用来签名为 TLS BootStrap 创建的证书和私钥
++ --root-ca-file 用来对 kube-apiserver 证书进行校验,指定该参数后,才会在Pod 容器的 ServiceAccount 中放置该 CA 证书文件
++ --leader-elect=true 使用多节点选主的方式选择主节点。只有主节点才会启动所有控制器,而其他从节点则仅执行选主算法
+
+### 创建scheduler 的服务文件
+
+``` bash
+[Unit]
+Description=Kubernetes Scheduler
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+
+[Service]
+ExecStart={{ bin_dir }}/kube-scheduler \
+ --address=127.0.0.1 \
+ --master=http://127.0.0.1:8080 \
+ --leader-elect=true \
+ --v=2
+Restart=on-failure
+RestartSec=5
+
+[Install]
+WantedBy=multi-user.target
+```
+
++ --address 同样值必须为 127.0.0.1
++ --master=http://127.0.0.1:8080 使用非安全 8080 端口与 kube-apiserver 通信
++ --leader-elect=true 部署多台机器组成的 master 集群时选举产生一个处于工作状态的 kube-controller-manager 进程
+
+### 在master 节点安装 node 服务: kubelet kube-proxy
+
+项目master 分支使用 DaemonSet 方式安装网络插件,如果master 节点不安装 kubelet 服务是无法安装网络插件的,如果 master 节点不安装网络插件,那么通过`apiserver` 方式无法访问 `dashboard` `kibana`等管理界面,[ISSUES #130](https://github.com/gjmzj/kubeasz/issues/130)
+
+项目v1.8 分支使用二进制方式安装网络插件,所以没有这个问题
+
+``` bash
+# vi 04.kube-master.yml
+- hosts: kube-master
+ roles:
+ - kube-master
+ - kube-node
+ # 禁止业务 pod调度到 master节点
+ tasks:
+ - name: 禁止业务 pod调度到 master节点
+ shell: "{{ bin_dir }}/kubectl cordon {{ inventory_hostname }} "
+ when: DEPLOY_MODE != "allinone"
+ ignore_errors: true
+```
+在master 节点也同时成为 node 节点后,默认业务 POD也会调度到 master节点,多主模式下这显然增加了 master节点的负载,因此可以使用 `kubectl cordon`命令禁止业务 POD调度到 master节点
+
+
+### master 集群的验证
+
+运行 `ansible-playbook 04.kube-master.yml` 成功后,验证 master节点的主要组件:
+
+``` bash
+# 查看进程状态
+systemctl status kube-apiserver
+systemctl status kube-controller-manager
+systemctl status kube-scheduler
+# 查看进程运行日志
+journalctl -u kube-apiserver
+journalctl -u kube-controller-manager
+journalctl -u kube-scheduler
+```
+执行 `kubectl get componentstatus` 可以看到
+
+``` bash
+NAME STATUS MESSAGE ERROR
+scheduler Healthy ok
+controller-manager Healthy ok
+etcd-0 Healthy {"health": "true"}
+etcd-2 Healthy {"health": "true"}
+etcd-1 Healthy {"health": "true"}
+```
+
+[后一篇](05-install_kube_node.md)
diff --git a/docs/setup/05-install_kube_node.md b/docs/setup/05-install_kube_node.md
new file mode 100644
index 0000000..db97e60
--- /dev/null
+++ b/docs/setup/05-install_kube_node.md
@@ -0,0 +1,142 @@
+## 05-安装kube-node节点
+
+`kube-node` 是集群中承载应用的节点,前置条件需要先部署好`kube-master`节点(因为需要操作`用户角色绑定`、`批准kubelet TLS 证书请求`等),它需要部署如下组件:
+
++ docker:运行容器
++ calico: 配置容器网络 (或者 flannel)
++ kubelet: kube-node上最主要的组件
++ kube-proxy: 发布应用服务与负载均衡
+
+``` bash
+roles/kube-node
+├── tasks
+│ └── main.yml
+└── templates
+ ├── cni-default.conf.j2
+ ├── kubelet.service.j2
+ ├── kubelet-csr.json.j2
+ └── kube-proxy.service.j2
+```
+
+请在另外窗口打开[roles/kube-node/tasks/main.yml](../../roles/kube-node/tasks/main.yml) 文件,对照看以下讲解内容。
+
+### 创建cni 基础网络插件配置文件
+
+因为后续需要用 `DaemonSet Pod`方式运行k8s网络插件,所以kubelet.server服务必须开启cni相关参数,并且提供cni网络配置文件
+
+### 创建 kubelet 的服务文件
+
++ 必须先创建工作目录 `/var/lib/kubelet`
+
+``` bash
+[Unit]
+Description=Kubernetes Kubelet
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+After=docker.service
+Requires=docker.service
+
+[Service]
+WorkingDirectory=/var/lib/kubelet
+#--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest
+ExecStart={{ bin_dir }}/kubelet \
+ --address={{ inventory_hostname }} \
+ --allow-privileged=true \
+ --anonymous-auth=false \
+ --authentication-token-webhook \
+ --authorization-mode=Webhook \
+ --pod-manifest-path=/etc/kubernetes/manifest \
+ --client-ca-file={{ ca_dir }}/ca.pem \
+ --cluster-dns={{ CLUSTER_DNS_SVC_IP }} \
+ --cluster-domain={{ CLUSTER_DNS_DOMAIN }} \
+ --cni-bin-dir={{ bin_dir }} \
+ --cni-conf-dir=/etc/cni/net.d \
+ --fail-swap-on=false \
+ --hairpin-mode hairpin-veth \
+ --hostname-override={{ inventory_hostname }} \
+ --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \
+ --max-pods={{ MAX_PODS }} \
+ --network-plugin=cni \
+ --pod-infra-container-image=mirrorgooglecontainers/pause-amd64:3.1 \
+ --register-node=true \
+ --root-dir={{ KUBELET_ROOT_DIR }} \
+ --tls-cert-file={{ ca_dir }}/kubelet.pem \
+ --tls-private-key-file={{ ca_dir }}/kubelet-key.pem \
+ --v=2
+#kubelet cAdvisor 默认在所有接口监听 4194 端口的请求, 以下iptables限制内网访问
+ExecStartPost=/sbin/iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 4194 -j ACCEPT
+ExecStartPost=/sbin/iptables -A INPUT -s 172.16.0.0/12 -p tcp --dport 4194 -j ACCEPT
+ExecStartPost=/sbin/iptables -A INPUT -s 192.168.0.0/16 -p tcp --dport 4194 -j ACCEPT
+ExecStartPost=/sbin/iptables -A INPUT -p tcp --dport 4194 -j DROP
+Restart=on-failure
+RestartSec=5
+
+[Install]
+WantedBy=multi-user.target
+```
++ --pod-infra-container-image 指定`基础容器`(负责创建Pod 内部共享的网络、文件系统等)镜像,**K8S每一个运行的 POD里面必然包含这个基础容器**,如果它没有运行起来那么你的POD 肯定创建不了,kubelet日志里面会看到类似 ` FailedCreatePodSandBox` 错误,可用`docker images` 查看节点是否已经下载到该镜像
++ --cluster-dns 指定 kubedns 的 Service IP(可以先分配,后续创建 kubedns 服务时指定该 IP),--cluster-domain 指定域名后缀,这两个参数同时指定后才会生效;
++ --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir={{ bin_dir }} 为使用cni 网络,并调用calico管理网络所需的配置
++ --fail-swap-on=false K8S 1.8+需显示禁用这个,否则服务不能启动
++ --client-ca-file={{ ca_dir }}/ca.pem 和 --anonymous-auth=false 关闭kubelet的匿名访问,详见[匿名访问漏洞说明](mixes/01.fix_kubelet_annoymous_access.md)
+
+### 创建 kube-proxy kubeconfig 文件
+
+该步骤已经在 deploy节点完成,[roles/deploy/tasks/main.yml](../../roles/deploy/tasks/main.yml)
+
++ 生成的kube-proxy.kubeconfig 配置文件需要移动到/etc/kubernetes/目录,后续kube-proxy服务启动参数里面需要指定
+
+### 创建 kube-proxy服务文件
+
+``` bash
+[Unit]
+Description=Kubernetes Kube-Proxy Server
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+After=network.target
+
+[Service]
+WorkingDirectory=/var/lib/kube-proxy
+ExecStart={{ bin_dir }}/kube-proxy \
+ --bind-address={{ inventory_hostname }} \
+ --hostname-override={{ inventory_hostname }} \
+ --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig \
+ --logtostderr=true \
+ --v=2
+Restart=on-failure
+RestartSec=5
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
+```
+
++ --hostname-override 参数值必须与 kubelet 的值一致,否则 kube-proxy 启动后会找不到该 Node,从而不会创建任何 iptables 规则
++ 特别注意:kube-proxy 根据 --cluster-cidr 判断集群内部和外部流量,指定 --cluster-cidr 或 --masquerade-all 选项后 kube-proxy 才会对访问 Service IP 的请求做 SNAT;但是这个特性与calico 实现 network policy冲突,所以如果要用 network policy,这两个选项都不要指定。
+
+### 批准kubelet 的 TLS 证书请求
+
+``` bash
+sleep 15 && {{ bin_dir }}/kubectl get csr|grep 'Pending' | awk 'NR>0{print $1}'| xargs {{ bin_dir }}/kubectl certificate approve
+```
++ 增加15秒延时等待kubelet启动
++ `kubectl get csr |grep 'Pending'` 找出待批准的 TLS请求
++ `kubectl certificate approve` 批准请求
+
+### 验证 node 状态
+
+``` bash
+systemctl status kubelet # 查看状态
+systemctl status kube-proxy
+journalctl -u kubelet # 查看日志
+journalctl -u kube-proxy
+```
+运行 `kubectl get node` 可以看到类似
+
+``` bash
+NAME STATUS ROLES AGE VERSION
+192.168.1.42 Ready 2d v1.9.0
+192.168.1.43 Ready 2d v1.9.0
+192.168.1.44 Ready 2d v1.9.0
+```
+
+
+[后一篇](06-install_network_plugin.md)
diff --git a/docs/setup/06-install_network_plugin.md b/docs/setup/06-install_network_plugin.md
new file mode 100644
index 0000000..212b1a4
--- /dev/null
+++ b/docs/setup/06-install_network_plugin.md
@@ -0,0 +1,42 @@
+## 06-安装网络组件
+
+首先回顾下K8S网络设计原则,在配置集群网络插件或者实践K8S 应用/服务部署请时刻想到这些原则:
+
+- 1.每个Pod都拥有一个独立IP地址,Pod内所有容器共享一个网络命名空间
+- 2.集群内所有Pod都在一个直接连通的扁平网络中,可通过IP直接访问
+ - 所有容器之间无需NAT就可以直接互相访问
+ - 所有Node和所有容器之间无需NAT就可以直接互相访问
+ - 容器自己看到的IP跟其他容器看到的一样
+- 3.Service cluster IP尽可在集群内部访问,外部请求需要通过NodePort、LoadBalance或者Ingress来访问
+
+`Container Network Interface (CNI)`是目前CNCF主推的网络模型,它由两部分组成:
+
+- CNI Plugin负责给容器配置网络,它包括两个基本的接口
+ - 配置网络: AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
+ - 清理网络: DelNetwork(net *NetworkConfig, rt *RuntimeConf) error
+- IPAM Plugin负责给容器分配IP地址
+
+Kubernetes Pod的网络是这样创建的:
+- 0.每个Pod除了创建时指定的容器外,都有一个kubelet启动时指定的`基础容器`,比如:`mirrorgooglecontainers/pause-amd64` `registry.access.redhat.com/rhel7/pod-infrastructure`
+- 1.首先 kubelet创建`基础容器`生成network namespace
+- 2.然后 kubelet调用网络CNI driver,由它根据配置调用具体的CNI 插件
+- 3.然后 CNI 插件给`基础容器`配置网络
+- 4.最后 Pod 中其他的容器共享使用`基础容器`的网络
+
+本项目基于CNI driver 调用各种网络插件来配置kubernetes的网络,常用CNI插件有 `flannel` `calico` `weave`等等,这些插件各有优势,也在互相借鉴学习优点,比如:在所有node节点都在一个二层网络时候,flannel提供hostgw实现,避免vxlan实现的udp封装开销,估计是目前最高效的;calico也针对L3 Fabric,推出了IPinIP的选项,利用了GRE隧道封装;因此这些插件都能适合很多实际应用场景。
+
+项目当前内置支持的网络插件有:`calico` `cilium` `flannel` `kube-router`
+
+### 安装讲解
+
+- [安装calico](network-plugin/calico.md)
+- [安装cilium](network-plugin/cilium.md)
+- [安装flannel](network-plugin/flannel.md)
+- [安装kube-router](network-plugin/kube-router.md)
+
+### 参考
+- [kubernetes.io networking docs](https://kubernetes.io/docs/concepts/cluster-administration/networking/)
+- [feiskyer-kubernetes指南网络章节](https://github.com/feiskyer/kubernetes-handbook/blob/master/zh/network/network.md)
+
+
+[后一篇](07-install_cluster_addon.md)
diff --git a/docs/setup/07-install_cluster_addon.md b/docs/setup/07-install_cluster_addon.md
new file mode 100644
index 0000000..a664d43
--- /dev/null
+++ b/docs/setup/07-install_cluster_addon.md
@@ -0,0 +1,16 @@
+# 07-安装集群主要插件
+
+目前挑选一些常用、必要的插件自动集成到安装脚本之中:
+- [自动脚本](../../roles/cluster-addon/tasks/main.yml)
+- 配置开关
+ - 参照[配置指南](config_guide.md),生成后在`roles/cluster-addon/defaults/main.yml`配置
+
+## 脚本介绍
+
+- 1.根据hosts文件中配置的`CLUSTER_DNS_SVC_IP` `CLUSTER_DNS_DOMAIN`等参数生成kubedns.yaml和coredns.yaml文件
+- 2.注册变量pod_info,pod_info用来判断现有集群是否已经运行各种插件
+- 3.根据pod_info和`配置开关`逐个进行/跳过插件安装
+
+
+
+[后一篇](08-cluster-storage.md)
diff --git a/docs/setup/08-cluster-storage.md b/docs/setup/08-cluster-storage.md
new file mode 100644
index 0000000..168b654
--- /dev/null
+++ b/docs/setup/08-cluster-storage.md
@@ -0,0 +1,99 @@
+# K8S 集群存储
+
+## 前言
+在kubernetes(k8s)中对于存储的资源抽象了两个概念,分别是PersistentVolume(PV)、PersistentVolumeClaim(PVC)。
+- PV是集群中的资源
+- PVC是对这些资源的请求。
+
+如上面所说PV和PVC都只是抽象的概念,在k8s中是通过插件的方式提供具体的存储实现。目前包含有NFS、iSCSI和云提供商指定的存储系统,更多的存储实现[参考官方文档](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes)。
+
+这里PV又有两种提供方式: 静态或者动态。
+本篇以介绍 **NFS存储** 为例,讲解k8s 众多存储方案中的一个实现。
+
+## 静态 PV
+首先我们需要一个NFS服务器,用于提供底层存储。通过文档[nfs-server](../guide/nfs-server.md),我们可以创建一个NFS服务器。
+
+- 创建静态 pv,指定容量,访问模式,回收策略,存储类等;参考[这里](https://github.com/feiskyer/kubernetes-handbook/blob/master/zh/concepts/persistent-volume.md)
+
+``` bash
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: pv-es-0
+spec:
+ capacity:
+ storage: 4Gi
+ accessModes:
+ - ReadWriteMany
+ volumeMode: Filesystem
+ persistentVolumeReclaimPolicy: Recycle
+ storageClassName: "es-storage-class"
+ nfs:
+ # 根据实际共享目录修改
+ path: /share/es0
+ # 根据实际 nfs服务器地址修改
+ server: 192.168.1.208
+```
+- 创建 pvc即可绑定使用上述 pv了,具体请看后文 test pod例子
+
+## 创建动态PV
+
+在一个工作k8s 集群中,`PVC`请求会很多,如果每次都需要管理员手动去创建对应的 `PV`资源,那就很不方便;因此 K8S还提供了多种 `provisioner`来动态创建 `PV`,不仅节省了管理员的时间,还可以根据`StorageClasses`封装不同类型的存储供 PVC 选用。
+
+项目中的 `role: cluster-storage`目前支持自建nfs 和aliyun_nas 的动态`provisioner`
+
+- 1.编辑自定义配置文件:上述命令执行后生成的roles/cluster-storage/defaults/main.yml
+
+``` bash
+# 比如创建nfs provisioner
+storage:
+ nfs:
+ enabled: "yes"
+ server: "192.168.1.8"
+ server_path: "/data/nfs"
+ storage_class: "class-nfs-01"
+ provisioner_name: "nfs-provisioner-01"
+```
+- 3.创建 nfs provisioner
+
+``` bash
+$ ansible-playbook /etc/ansible/roles/cluster-storage/cluster-storage.yml
+# 执行成功后验证
+$ kubectl get pod --all-namespaces |grep nfs-prov
+kube-system nfs-provisioner-01-6b7fbbf9d4-bh8lh 1/1 Running 0 1d
+```
+**注意** k8s集群可以使用多个nfs provisioner,重复上述步骤2 修改使用不同的`nfs server` `nfs_storage_class` `nfs_provisioner_name`后执行步骤3创建即可。
+
+## 验证使用动态 PV
+
+切换到项目`manifests/storage`目录,编辑`test.yaml`文件,根据前文配置情况修改`storageClassName`即可;然后执行以下命令进行创建:
+
+``` bash
+$ kubectl apply -f test.yaml
+
+# 验证测试pod
+$ kubectl get pod --all-namespaces |grep test
+default test 1/1 Running 0 1m
+
+# 验证自动创建的pv 资源,
+$ kubectl get pv
+NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
+pvc-8f1b4ced-92d2-11e8-a41f-5254008ec7c0 1Mi RWX Delete Bound default/test-claim nfs-dynamic-class-01 3m
+
+# 验证PVC已经绑定成功:STATUS字段为 Bound
+$ kubectl get pvc
+NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
+test-claim Bound pvc-8f1b4ced-92d2-11e8-a41f-5254008ec7c0 1Mi RWX nfs-dynamic-class-01 3m
+```
+
+另外,Pod启动完成后,在挂载的目录中创建一个`SUCCESS`文件。我们可以到NFS服务器去看下:
+
+```
+.
+└── default-test-claim-pvc-a877172b-5f49-11e8-b675-d8cb8ae6325a
+ └── SUCCESS
+```
+如上,可以发现挂载的时候,nfs-client根据PVC自动创建了一个目录,我们Pod中挂载的`/mnt`,实际引用的就是该目录,而我们在`/mnt`下创建的`SUCCESS`文件,也自动写入到了这里。
+
+# 后续
+后面当我们需要为上层应用提供持久化存储时,只需要提供`StorageClass`即可。很多应用都会根据`StorageClass`来创建他们的所需的PVC, 最后再把PVC挂载到他们的Deployment或StatefulSet中使用,比如:efk、jenkins等
diff --git a/docs/setup/config_guide.md b/docs/setup/config_guide.md
new file mode 100644
index 0000000..1b4168a
--- /dev/null
+++ b/docs/setup/config_guide.md
@@ -0,0 +1,31 @@
+# 个性化集群参数配置
+
+对于刚接触项目者,如"快速指南"说明,只需要:
+
+- **1** 个配置:`/etc/ansible/hosts`
+- **1** 键安装:`ansible-playbook /etc/ansilbe/90.setup.yml`
+
+具体来讲 `kubeasz`创建集群主要在以下两个地方进行配置:
+
+- ansible hosts 文件(模板在examples目录):集群主要节点定义和主要参数配置、全局变量
+- roles/xxx/defaults/main.yml 文件:其他参数配置或者部分组件附加参数
+
+## ansible hosts
+
+项目在[快速指南](quickStart.md)或者[集群规划与安装概览](00-planning_and_overall_intro.md)已经介绍过,主要包括集群节点定义和集群范围的主要参数配置;目前提供四种集群部署模板。
+
+- 尽量保持配置简单灵活
+- 尽量保持配置项稳定
+
+## roles/xxx/defaults/main.yml
+
+主要包括集群某个具体组件的个性化配置,具体组件的配置项可能会不断增加;
+
+- 可以在不做任何配置更改情况下使用默认值创建集群
+- 可以根据实际需要配置 k8s 集群,常用举例
+ - 配置 kube-proxy 使用 ipvs:修改 roles/kube-node/defaults/main.yml 变量 PROXY_MODE: "ipvs"
+ - 配置 lb 节点负载均衡算法:修改 roles/lb/defaults/main.yml 变量 BALANCE_ALG: "roundrobin"
+ - 配置 docker 国内镜像加速站点:修改 roles/docker/defaults/main.yml 相关变量
+ - 配置 apiserver 支持公网域名:修改 roles/kube-master/defaults/main.yml 相关变量
+ - 配置 flannel 使用镜像版本:修改 roles/flannel/defaults/main.yml 相关变量
+ - 配置选择不同 addon 组件:修改roles/cluster-addon/defaults/main.yml
diff --git a/docs/setup/docker_kubeasz.md b/docs/setup/docker_kubeasz.md
new file mode 100644
index 0000000..0bdb4cb
--- /dev/null
+++ b/docs/setup/docker_kubeasz.md
@@ -0,0 +1,73 @@
+# 容器化运行 kubeasz
+
+## TL;DR;
+
+- 1.本机安装 docker (略)
+- 2.配置 ssh 密钥登陆集群节点
+
+``` bash
+ssh-keygen -t rsa -b 2048 回车 回车 回车
+ssh-copy-id $IP # $IP 为所有节点地址包括自身,按照提示输入 yes 和 root 密码
+```
+
+- 3.下载 kubeasz docker 镜像并运行
+
+``` bash
+export KUBEASZ_VER=1.0.0rc1
+docker pull jmgao1983/kubeasz:$KUBEASZ_VER
+wget https://github.com/gjmzj/kubeasz/releases/download/$KUBEASZ_VER/kubeasz-docker
+bash kubeasz-docker start $KUBEASZ_VER
+```
+
+- 4.在 kubeasz 容器中创建 k8s 集群,步骤与非容器方式创建类似
+
+``` bash
+# 进入容器后,在 /etc/ansible 目录配置 hosts等,然后创建集群(与非容器方式一致)
+docker exec -it kubeasz sh
+# 举例1:快速创建单节点集群
+docker exec -t kubeasz easzctl start aio
+```
+
+## 验证
+
+使用容器化安装成功后,可以在 **容器内** 或者 **宿主机** 上执行 kubectl 命令验证集群状态。
+
+## kubeasz 镜像介绍
+
+镜像描述文件 dockerfiles/kubeasz/Dockerfile,它基于 ansible 镜像(dockerfiles/ansible/Dockerfile),主要包含 kubeasz 项目代码和 k8s 集群安装所需二进制文件。
+
+- 在本地创建 kubeasz 镜像,由于镜像较大,可以按以下步骤在本地创建
+
+``` bash
+cd /etc/ansible/dockerfiles/kubeasz
+# 克隆代码
+git clone https://github.com/gjmzj/kubeasz.git
+# 手动下载二进制文件放入上述 git clone 完成目录 kubeasz/bin
+docker build -t kubeasz:$TAG .
+```
+
+## 容器运行讲解
+
+容器启动脚本详见文件 tools/kubeasz_docker
+
+``` bash
+docker run --detach \
+ --name kubeasz \
+ --restart always \
+ --env KUBEASZ_DOCKER_HOST=$HOST_IP \
+ --volume /etc/ansible:/etc/ansible \
+ --volume /root/.kube:/root/.kube \
+ --volume /root/.ssh/id_rsa:/root/.ssh/id_rsa:ro \
+ --volume /root/.ssh/id_rsa.pub:/root/.ssh/id_rsa.pub:ro \
+ --volume /root/.ssh/known_hosts:/root/.ssh/known_hosts:ro \
+ $KUBEASZ_DOCKER_VER
+```
+
+- --env KUBEASZ_DOCKER_HOST=$HOST_IP 传递这个参数是为了快速在本机安装aio集群
+- --volume /etc/ansible:/etc/ansible 挂载本地目录,这样可以在宿主机上修改集群配置,然后在容器内执行 ansible 安装
+- --volume /root/.kube:/root/.kube 容器内与主机共享 kubeconfig,这样都可以执行 kubectl 命令
+- --volume /root/.ssh/id_rsa:/root/.ssh/id_rsa:ro 等三个 volume 挂载保证:如果宿主机配置了免密码登陆所有集群节点,那么容器内也可以免密码登陆所有节点
+
+## 参考
+
+- ansible 容器镜像制作: https://github.com/William-Yeh/docker-ansible
diff --git a/docs/setup/kubeasz_on_public_cloud.md b/docs/setup/kubeasz_on_public_cloud.md
new file mode 100644
index 0000000..a4c8f3d
--- /dev/null
+++ b/docs/setup/kubeasz_on_public_cloud.md
@@ -0,0 +1,79 @@
+# 公有云上部署 kubeasz
+
+在公有云上使用`kubeasz`部署`k8s`集群需要注意以下几点:
+
+1. 注意虚机的安全组规则配置,一般集群内部节点之间端口放开即可;
+
+2. 部分`k8s`网络组件受限,一般可以选择 flannel (vxlan模式)、calico(开启ipinip);
+
+3. 无法自由创建`lb`节点,一般使用云负载均衡(内网)四层TCP负载模式;
+
+4. 部分云厂商负载均衡使用四层负载模式时不支持添加进后端云服务器池的 ECS 既作为 Real Server,又作为客户端向所在的 SLB 实例发送请求;因此注意不要在 master节点执行 kubectl,会出现时通时不通的情况;
+
+## 在公有云上部署多主多节点集群
+
+- 单节点和单主多节点集群的节点规划与自有环境没有差异
+
+- 多主多节点集群节点规划不需要lb节点
+
+其他在公有云上的安装步骤与自有环境没有差异,节点规划可以参考 [example/hosts.cloud.example](../../example/hosts.cloud.example),如下:(避免deploy节点同时作为master节点)
+
+``` bash
+# 集群部署节点:一般为运行ansible 脚本的节点
+# 变量 NTP_ENABLED (=yes/no) 设置集群是否安装 chrony 时间同步, 公有云上虚机不需要
+[deploy]
+10.1.0.160 NTP_ENABLED=no
+
+# etcd集群请提供如下NODE_NAME,注意etcd集群必须是1,3,5,7...奇数个节点
+[etcd]
+10.1.0.160 NODE_NAME=etcd1
+10.1.0.161 NODE_NAME=etcd2
+10.1.0.162 NODE_NAME=etcd3
+
+[kube-master]
+10.1.0.161
+10.1.0.162
+
+# 公有云上一般都有提供负载均衡产品,且不允许自己创建,lb 节点留空,仅保留组名
+[lb]
+
+[kube-node]
+10.1.0.160
+10.1.0.163
+
+# 参数 NEW_INSTALL:yes表示新建,no表示使用已有harbor服务器
+[harbor]
+#10.1.0.8 HARBOR_DOMAIN="harbor.yourdomain.com" NEW_INSTALL=no
+
+...
+```
++ 创建云负载均衡,例如阿里云slb如下:
+
+``` bash
+1. 首先创建SLB,注意选择【可用区】,【实例类型】可以先选‘私网’,【网络类型】专有网络,【虚拟交换机】跟你k8s集群节点同一交换机
+2. 配置【协议&监听】TCP 【端口】8443,【后端服务器】即 master 节点服务器,端口 6443
+3. 配置完成,记下负载均衡的内部地址(例如 10.1.0.200)
+```
++ 继续配置 ansible hosts,设置`MASTER_IP` 为刚才创建的SLB地址
+
+``` bash
+[all:vars]
+# ---------集群主要参数---------------
+#集群部署模式:allinone, single-master, multi-master
+DEPLOY_MODE=multi-master
+
+# 创建内网云负载均衡,然后配置:前端监听 tcp 8443,后端 tcp 6443,后端节点即 master 节点
+MASTER_IP="10.1.0.200" # 即负载均衡内网地址
+KUBE_APISERVER="https://{{ MASTER_IP }}:8443"
+
+# 集群网络插件,目前支持calico, flannel
+CLUSTER_NETWORK="flannel"
+
+...
+```
++ 一步创建集群 `ansible-playbook /etc/ansible/90.setup.yml`
+
+### 其他资料
+
+另外由[li-sen](https://github.com/li-sen)分享的[kubeasz-阿里云vpc部署记录](https://li-sen.github.io/post/blog-wiki/2018-09-27-k8s-kubeasz-%E9%98%BF%E9%87%8C%E4%BA%91vpc%E9%83%A8%E7%BD%B2%E8%AE%B0%E5%BD%95/):介绍了阿里云上自建高可用k8s集群碰过的问题与解决,主要是使用一台haproxy中转解决slb的限制问题。
+
diff --git a/docs/setup/network-plugin/calico-bgp-rr.md b/docs/setup/network-plugin/calico-bgp-rr.md
new file mode 100644
index 0000000..d6a4099
--- /dev/null
+++ b/docs/setup/network-plugin/calico-bgp-rr.md
@@ -0,0 +1,263 @@
+# calico 配置 BGP Route Reflectors
+
+`Calico`作为`k8s`的一个流行网络插件,它依赖`BGP`路由协议实现集群节点上的`POD`路由互通;而路由互通的前提是节点间建立 BGP Peer 连接。BGP 路由反射器(Route Reflectors,简称 RR)可以简化集群BGP Peer的连接方式,它是解决BGP扩展性问题的有效方式;具体来说:
+
+- 没有 RR 时,所有节点之间需要两两建立连接(IBGP全互联),节点数量增加将导致连接数剧增、资源占用剧增
+- 引入 RR 后,其他 BGP 路由器只需要与它建立连接并交换路由信息,节点数量增加连接数只是线性增加,节省系统资源
+
+calico-node 版本 v3.3 开始支持内建路由反射器,非常方便,因此使用 calico 作为网络插件可以支持大规模节点数的`K8S`集群。
+
+本文档主要讲解配置 BGP Route Reflectors,建议首先阅读[基础calico文档](calico.md)。
+
+## 前提条件
+
+实验环境为按照kubeasz安装的2主2从集群,calico 版本 v3.3.2
+
+```
+$ kubectl get node
+NAME STATUS ROLES AGE VERSION
+192.168.1.1 Ready,SchedulingDisabled master 178m v1.13.1
+192.168.1.2 Ready,SchedulingDisabled master 178m v1.13.1
+192.168.1.3 Ready node 178m v1.13.1
+192.168.1.4 Ready node 178m v1.13.1
+$ kubectl get pod -n kube-system -o wide | grep calico
+calico-kube-controllers-77487546bd-jqrlc 1/1 Running 0 179m 192.168.1.3 192.168.1.3
+calico-node-67t5m 2/2 Running 0 179m 192.168.1.1 192.168.1.1
+calico-node-drmhq 2/2 Running 0 179m 192.168.1.2 192.168.1.2
+calico-node-rjtkv 2/2 Running 0 179m 192.168.1.4 192.168.1.4
+calico-node-xtspl 2/2 Running 0 179m 192.168.1.3 192.168.1.3
+```
+查看当前集群中BGP连接情况:可以看到集群中4个节点两两建立了 BGP 连接
+
+```
+$ ansible all -m shell -a '/opt/kube/bin/calicoctl node status'
+192.168.1.3 | SUCCESS | rc=0 >>
+Calico process is running.
+
+IPv4 BGP status
++--------------+-------------------+-------+----------+-------------+
+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
++--------------+-------------------+-------+----------+-------------+
+| 192.168.1.1 | node-to-node mesh | up | 03:08:20 | Established |
+| 192.168.1.2 | node-to-node mesh | up | 03:08:18 | Established |
+| 192.168.1.4 | node-to-node mesh | up | 03:08:19 | Established |
++--------------+-------------------+-------+----------+-------------+
+
+IPv6 BGP status
+No IPv6 peers found.
+
+192.168.1.2 | SUCCESS | rc=0 >>
+Calico process is running.
+
+IPv4 BGP status
++--------------+-------------------+-------+----------+-------------+
+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
++--------------+-------------------+-------+----------+-------------+
+| 192.168.1.4 | node-to-node mesh | up | 03:08:17 | Established |
+| 192.168.1.3 | node-to-node mesh | up | 03:08:18 | Established |
+| 192.168.1.1 | node-to-node mesh | up | 03:08:20 | Established |
++--------------+-------------------+-------+----------+-------------+
+
+IPv6 BGP status
+No IPv6 peers found.
+
+192.168.1.1 | SUCCESS | rc=0 >>
+Calico process is running.
+
+IPv4 BGP status
++--------------+-------------------+-------+----------+-------------+
+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
++--------------+-------------------+-------+----------+-------------+
+| 192.168.1.2 | node-to-node mesh | up | 03:08:21 | Established |
+| 192.168.1.3 | node-to-node mesh | up | 03:08:21 | Established |
+| 192.168.1.4 | node-to-node mesh | up | 03:08:21 | Established |
++--------------+-------------------+-------+----------+-------------+
+
+IPv6 BGP status
+No IPv6 peers found.
+
+192.168.1.4 | SUCCESS | rc=0 >>
+Calico process is running.
+
+IPv4 BGP status
++--------------+-------------------+-------+----------+-------------+
+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
++--------------+-------------------+-------+----------+-------------+
+| 192.168.1.2 | node-to-node mesh | up | 03:08:17 | Established |
+| 192.168.1.3 | node-to-node mesh | up | 03:08:19 | Established |
+| 192.168.1.1 | node-to-node mesh | up | 03:08:20 | Established |
++--------------+-------------------+-------+----------+-------------+
+
+IPv6 BGP status
+No IPv6 peers found.
+```
+## 配置全局禁用全连接(BGP full mesh)
+
+```
+$ cat << EOF | calicoctl create -f -
+apiVersion: projectcalico.org/v3
+kind: BGPConfiguration
+metadata:
+ name: default
+spec:
+ logSeverityScreen: Info
+ nodeToNodeMeshEnabled: false
+ asNumber: 64512
+EOF
+```
+
+上述命令配置完成后,再次使用命令`ansible all -m shell -a '/opt/kube/bin/calicoctl node status'`查看,可以看到之前所有的bgp连接都消失了。
+
+## 配置 BGP node 与 Route Reflector 的连接建立规则
+
+``` bash
+$ cat << EOF | calicoctl create -f -
+kind: BGPPeer
+apiVersion: projectcalico.org/v3
+metadata:
+ name: peer-to-rrs
+spec:
+ # 规则1:普通 bgp node 与 rr 建立连接
+ nodeSelector: !has(i-am-a-route-reflector)
+ peerSelector: has(i-am-a-route-reflector)
+
+---
+kind: BGPPeer
+apiVersion: projectcalico.org/v3
+metadata:
+ name: rr-mesh
+spec:
+ # 规则2:route reflectors 之间也建立连接
+ nodeSelector: has(i-am-a-route-reflector)
+ peerSelector: has(i-am-a-route-reflector)
+EOF
+```
+
+上述命令配置完成后,使用命令:`calicoctl get bgppeer` `calicoctl get bgppeer rr-mesh -o yaml` 检查配置是否正确。
+
+## 选择并配置 Route Reflector 节点
+
+首先查看当前集群中的节点:
+
+```
+$ calicoctl get node -o wide
+NAME ASN IPV4 IPV6
+k8s401 (64512) 192.168.1.1/24
+k8s402 (64512) 192.168.1.2/24
+k8s403 (64512) 192.168.1.3/24
+k8s404 (64512) 192.168.1.4/24
+```
+
+可以在集群中选择1个或多个节点作为 rr 节点,这里先选择节点:k8s401
+
+``` bash
+# 1.先导出 node k8s401 的配置,准备修改
+$ calicoctl get node k8s401 --export -o yaml |tee rr01.yml
+apiVersion: projectcalico.org/v3
+kind: Node
+metadata:
+ creationTimestamp: null
+ name: k8s401
+spec:
+ bgp:
+ ipv4Address: 192.168.1.1/24
+ ipv4IPIPTunnelAddr: 172.20.7.128
+ orchRefs:
+ - nodeName: 192.168.1.1
+ orchestrator: k8s
+
+# 2.修改上述 rr01.yml 的配置如下
+apiVersion: projectcalico.org/v3
+kind: Node
+metadata:
+ creationTimestamp: null
+ name: k8s401
+ labels:
+ # 设置标签
+ i-am-a-route-reflector: true
+spec:
+ bgp:
+ ipv4Address: 192.168.1.1/24
+ ipv4IPIPTunnelAddr: 172.20.7.128
+ # 设置集群ID
+ routeReflectorClusterID: 224.0.0.1
+ orchRefs:
+ - nodeName: 192.168.1.1
+ orchestrator: k8s
+
+# 3.应用修改后的 rr node 配置
+$ calicoctl apply -f rr01.yml
+```
+
+## 查看增加 rr 之后的bgp 连接情况
+
+```
+$ ansible all -m shell -a '/opt/kube/bin/calicoctl node status'
+192.168.1.4 | SUCCESS | rc=0 >>
+Calico process is running.
+
+IPv4 BGP status
++--------------+-----------+-------+----------+-------------+
+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
++--------------+-----------+-------+----------+-------------+
+| 192.168.1.1 | global | up | 11:02:55 | Established |
++--------------+-----------+-------+----------+-------------+
+
+IPv6 BGP status
+No IPv6 peers found.
+
+192.168.1.3 | SUCCESS | rc=0 >>
+Calico process is running.
+
+IPv4 BGP status
++--------------+-----------+-------+----------+-------------+
+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
++--------------+-----------+-------+----------+-------------+
+| 192.168.1.1 | global | up | 11:02:55 | Established |
++--------------+-----------+-------+----------+-------------+
+
+IPv6 BGP status
+No IPv6 peers found.
+
+192.168.1.1 | SUCCESS | rc=0 >>
+Calico process is running.
+
+IPv4 BGP status
++--------------+---------------+-------+----------+-------------+
+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
++--------------+---------------+-------+----------+-------------+
+| 192.168.1.2 | node specific | up | 11:02:55 | Established |
+| 192.168.1.3 | node specific | up | 11:02:55 | Established |
+| 192.168.1.4 | node specific | up | 11:02:55 | Established |
++--------------+---------------+-------+----------+-------------+
+
+IPv6 BGP status
+No IPv6 peers found.
+
+192.168.1.2 | SUCCESS | rc=0 >>
+Calico process is running.
+
+IPv4 BGP status
++--------------+-----------+-------+----------+-------------+
+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
++--------------+-----------+-------+----------+-------------+
+| 192.168.1.1 | global | up | 11:02:55 | Established |
++--------------+-----------+-------+----------+-------------+
+
+IPv6 BGP status
+No IPv6 peers found.
+```
+可以看到所有其他节点都与所选rr节点建立bgp连接。
+
+## 再增加一个 rr 节点
+
+步骤同上述选择第1个 rr 节点,这里省略;添加成功后可以看到所有其他节点都与两个rr节点建立bgp连接,两个rr节点之间也建立bgp连接。
+
+- 对于节点数较多的`K8S`集群建议配置3-4个 RR 节点
+
+## 参考文档
+
+- 1.[Calico 使用指南:Route Reflectors](https://docs.projectcalico.org/v3.3/usage/routereflector)
+- 2.[BGP路由反射器基础](https://www.sohu.com/a/140033025_761420)
+
+更多 BGP 路由协议相关知识请查阅思科/华为相关网络文档。
diff --git a/docs/setup/network-plugin/calico.md b/docs/setup/network-plugin/calico.md
new file mode 100644
index 0000000..6e1e360
--- /dev/null
+++ b/docs/setup/network-plugin/calico.md
@@ -0,0 +1,166 @@
+## 06-安装calico网络组件.md
+
+推荐阅读[calico kubernetes guide](https://docs.projectcalico.org/v3.0/getting-started/kubernetes/)
+
+本项目提供多种网络插件可选,如果需要安装calico,请在/etc/ansible/hosts文件中设置变量 `CLUSTER_NETWORK="calico"`,更多的calico设置在`roles/calico/defaults/main.yml`文件定义。
+
+- calico-node需要在所有master节点和node节点安装
+
+``` bash
+roles/calico/
+├── tasks
+│ └── main.yml
+└── templates
+ ├── calico-csr.json.j2
+ ├── calicoctl.cfg.j2
+ └── calico.yaml.j2
+```
+请在另外窗口打开[roles/calico/tasks/main.yml](../roles/calico/tasks/main.yml) 文件,对照看以下讲解内容。
+
+### 创建calico 证书申请
+
+``` bash
+{
+ "CN": "calico",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
+```
+- calico 使用客户端证书,所以hosts字段可以为空;后续可以看到calico证书用在四个地方:
+ - calico/node 这个docker 容器运行时访问 etcd 使用证书
+ - cni 配置文件中,cni 插件需要访问 etcd 使用证书
+ - calicoctl 操作集群网络时访问 etcd 使用证书
+ - calico/kube-controllers 同步集群网络策略时访问 etcd 使用证书
+
+### 创建 calico DaemonSet yaml文件和rbac 文件
+
+请对照 roles/calico/templates/calico.yaml.j2文件注释和以下注意内容
+
++ 详细配置参数请参考[calico官方文档](https://docs.projectcalico.org/v2.6/reference/node/configuration)
++ calico-node是以docker容器运行在host上的,因此需要把之前的证书目录 /etc/calico/ssl挂载到容器中
++ 配置ETCD_ENDPOINTS 、CA、证书等,所有{{ }}变量与ansible hosts文件中设置对应
++ 配置集群POD网络 CALICO_IPV4POOL_CIDR={{ CLUSTER_CIDR }}
++ **重要**本K8S集群运行在同网段kvm虚机上,虚机间没有网络ACL限制,因此可以设置`CALICO_IPV4POOL_IPIP=off`,如果你的主机位于不同网段,或者运行在公有云上需要打开这个选项 `CALICO_IPV4POOL_IPIP=always`
++ 配置FELIX_DEFAULTENDPOINTTOHOSTACTION=ACCEPT 默认允许Pod到Node的网络流量,更多[felix配置选项](https://docs.projectcalico.org/v2.6/reference/felix/configuration)
++ 多网卡服务器运行kube-controller可能会出现问题,本项目已通过增加设置环境变量`KUBERNETES_SERVICE_HOST`和`KUBERNETES_SERVICE_PORT`解决
+
+### 安装calico 网络
+
++ 安装前检查主机名不能有大写字母,只能由`小写字母` `-` `.` 组成 (name must consist of lower case alphanumeric characters, '-' or '.' (regex: [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*))(calico-node v3.0.6以上已经解决主机大写字母问题)
++ **安装前必须确保各节点主机名不重复** ,calico node name 由节点主机名决定,如果重复,那么重复节点在etcd中只存储一份配置,BGP 邻居也不会建立。
++ 安装之前必须确保`kube-master`和`kube-node`节点已经成功部署
++ 只需要在任意装有kubectl客户端的节点运行 `kubectl create `安装即可
++ 等待15s后(视网络拉取calico相关镜像速度),calico 网络插件安装完成,删除之前kube-node安装时默认cni网络配置
+
+### [可选]配置calicoctl工具 [calicoctl.cfg.j2](roles/calico/templates/calicoctl.cfg.j2)
+
+``` bash
+apiVersion: v1
+kind: calicoApiConfig
+metadata:
+spec:
+ datastoreType: "etcdv2"
+ etcdEndpoints: {{ ETCD_ENDPOINTS }}
+ etcdKeyFile: /etc/calico/ssl/calico-key.pem
+ etcdCertFile: /etc/calico/ssl/calico.pem
+ etcdCACertFile: /etc/calico/ssl/ca.pem
+```
+
+### 验证calico网络
+
+执行calico安装成功后可以验证如下:(需要等待镜像下载完成,有时候即便上一步已经配置了docker国内加速,还是可能比较慢,请确认以下容器运行起来以后,再执行后续验证步骤)
+
+``` bash
+kubectl get pod --all-namespaces
+NAMESPACE NAME READY STATUS RESTARTS AGE
+kube-system calico-kube-controllers-5c6b98d9df-xj2n4 1/1 Running 0 1m
+kube-system calico-node-4hr52 2/2 Running 0 1m
+kube-system calico-node-8ctc2 2/2 Running 0 1m
+kube-system calico-node-9t8md 2/2 Running 0 1m
+```
+
+**查看网卡和路由信息**
+
+先在集群创建几个测试pod: `kubectl run test --image=busybox --replicas=3 sleep 30000`
+
+``` bash
+# 查看网卡信息
+ip a
+```
+
++ 可以看到包含类似cali1cxxx的网卡,是calico为测试pod生成的
++ tunl0网卡现在不用管,是默认生成的,当开启IPIP 特性时使用的隧道
+
+``` bash
+# 查看路由
+route -n
+Kernel IP routing table
+Destination Gateway Genmask Flags Metric Ref Use Iface
+0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 ens3
+192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 ens3
+172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
+172.20.3.64 192.168.1.34 255.255.255.192 UG 0 0 0 ens3
+172.20.33.128 0.0.0.0 255.255.255.192 U 0 0 0 *
+172.20.33.129 0.0.0.0 255.255.255.255 UH 0 0 0 caliccc295a6d4f
+172.20.104.0 192.168.1.35 255.255.255.192 UG 0 0 0 ens3
+172.20.166.128 192.168.1.63 255.255.255.192 UG 0 0 0 ens3
+```
+
+**查看所有calico节点状态**
+
+``` bash
+calicoctl node status
+Calico process is running.
+
+IPv4 BGP status
++--------------+-------------------+-------+----------+-------------+
+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
++--------------+-------------------+-------+----------+-------------+
+| 192.168.1.34 | node-to-node mesh | up | 12:34:00 | Established |
+| 192.168.1.35 | node-to-node mesh | up | 12:34:00 | Established |
+| 192.168.1.63 | node-to-node mesh | up | 12:34:01 | Established |
++--------------+-------------------+-------+----------+-------------+
+```
+
+**BGP 协议是通过TCP 连接来建立邻居的,因此可以用netstat 命令验证 BGP Peer**
+
+``` bash
+netstat -antlp|grep ESTABLISHED|grep 179
+tcp 0 0 192.168.1.66:179 192.168.1.35:41316 ESTABLISHED 28479/bird
+tcp 0 0 192.168.1.66:179 192.168.1.34:40243 ESTABLISHED 28479/bird
+tcp 0 0 192.168.1.66:179 192.168.1.63:48979 ESTABLISHED 28479/bird
+```
+
+**查看etcd中calico相关信息**
+
+因为这里calico网络使用etcd存储数据,所以可以在etcd集群中查看数据
+
++ calico 3.x 版本默认使用 etcd v3存储,**登陆集群的一个etcd 节点**,查看命令:
+
+``` bash
+# 查看所有calico相关数据
+ETCDCTL_API=3 etcdctl --endpoints="http://127.0.0.1:2379" get --prefix /calico
+# 查看 calico网络为各节点分配的网段
+ETCDCTL_API=3 etcdctl --endpoints="http://127.0.0.1:2379" get --prefix /calico/ipam/v2/host
+```
+
++ calico 2.x 版本默认使用 etcd v2存储,**登陆集群的一个etcd 节点**,查看命令:
+
+``` bash
+# 查看所有calico相关数据
+etcdctl --endpoints=http://127.0.0.1:2379 --ca-file=/etc/kubernetes/ssl/ca.pem ls /calico
+```
+
+## 下一步:[设置 BGP Route Reflector](calico-bgp-rr.md)
diff --git a/docs/setup/network-plugin/cilium.md b/docs/setup/network-plugin/cilium.md
new file mode 100644
index 0000000..257a442
--- /dev/null
+++ b/docs/setup/network-plugin/cilium.md
@@ -0,0 +1,224 @@
+# 06-安装cilium网络组件
+
+`cilium` 是一个革新的网络与安全组件;基于 linux 内核新技术--`BPF`,它可以透明、零侵入地实现服务间安全策略与可视化,主要优势如下:
+
+- 支持L3/L4, L7(如:HTTP/gRPC/Kafka)的安全策略
+- 支持基于安全ID而不是地址+端口的传统防火墙策略
+- 支持基于Overlay或Native Routing的扁平多节点pod网络
+ - Overlay VXLAN 方式类似于 flannel 的VXLAN后端
+- 高性能负载均衡,支持DSR
+- 支持事件、策略跟踪和监控集成
+
+## 开始使用 cilium
+
+以下为简要翻译 `cilium doc`上的一个应用示例[原文](http://docs.cilium.io/en/stable/gettingstarted/minikube/#step-2-deploy-the-demo-application),部署在单节点k8s 环境的实践。
+
+### 0.升级内核并重启
+
+- Linux kernel >= 4.9.17,请阅读文档[升级内核](guide/kernel_upgrade.md)
+- etcd >= 3.1.0 or consul >= 0.6.4
+
+### 1.选择cilium网络后安装k8s(allinone)
+
+- 参考[快速指南](quickStart.md),设置 ansible hosts 文件中变量 `CLUSTER_NETWORK="cilium"`
+
+### 2.部署示例应用
+
+官方文档用几个`pod/svc` 抽象一个有趣的应用场景(星战迷):星战中帝国方建造了被称为“终极武器”的“死星”,它是一个卫星大小的战斗空间站,它的核心是使用凯伯晶体(Kyber Crystal)的超级激光炮,剧中它的首秀就以完全火力摧毁了“杰达圣城”(Jedha)。下面将用运行于 k8s上的 pod/svc/cilium 等模拟“死星“的一个“飞船登陆”系统安全策略设计。
+
+- deploy/deathstar:作为控制整个“死星”的飞船登陆管理系统,它暴露一个SVC,提供HTTP REST 接口给飞船请求登陆使用;
+- pod/tiefighter:作为“帝国”方的常规战斗飞船,它会调用上述 HTTP 接口,请求登陆“死星”;
+- pod/xwing:作为“盟军”方的飞行舰,它也尝试调用 HTTP 接口,请求登陆“死星”;
+
+
+
+根据文件[http-sw-app.yaml](../roles/cilium/files/star_war_example/http-sw-app.yaml) 创建 `$ kubectl create -f http-sw-app.yaml` 后,验证如下:
+
+``` bash
+$ kubectl get pods,svc
+NAME READY STATUS RESTARTS AGE
+pod/deathstar-5fc7c7795d-djf2q 1/1 Running 0 4h
+pod/deathstar-5fc7c7795d-hrgst 1/1 Running 0 4h
+pod/tiefighter 1/1 Running 0 4h
+pod/xwing 1/1 Running 0 4h
+
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+service/deathstar ClusterIP 10.68.242.130 80/TCP 4h
+service/kubernetes ClusterIP 10.68.0.1 443/TCP 5h
+```
+每个 POD 在 `cilium` 中都表示为 `Endpoint`,初始每个 `Endpoint` 的”进出安全策略“状态均为 `Disabled`,如下:(已省略部分无关 POD 信息)
+
+``` bash
+$ kubectl exec -n kube-system cilium-6t5vx -- cilium endpoint list
+ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
+ ENFORCEMENT ENFORCEMENT
+643 Disabled Disabled 31371 k8s:class=deathstar f00d::ac14:0:0:283 172.20.0.246 ready
+ k8s:io.cilium.k8s.policy.serviceaccount=default
+ k8s:io.kubernetes.pod.namespace=default
+ k8s:org=empire
+1011 Disabled Disabled 31371 k8s:class=deathstar f00d::ac14:0:0:3f3 172.20.0.63 ready
+ k8s:io.cilium.k8s.policy.serviceaccount=default
+ k8s:io.kubernetes.pod.namespace=default
+ k8s:org=empire
+32030 Disabled Disabled 5350 k8s:class=tiefighter f00d::ac14:0:0:7d1e 172.20.0.201 ready
+ k8s:io.cilium.k8s.policy.serviceaccount=default
+ k8s:io.kubernetes.pod.namespace=default
+ k8s:org=empire
+45943 Disabled Disabled 14309 k8s:class=xwing f00d::ac14:0:0:b377 172.20.0.189 ready
+ k8s:io.cilium.k8s.policy.serviceaccount=default
+ k8s:io.kubernetes.pod.namespace=default
+ k8s:org=alliance
+52035 Disabled Disabled 4 reserved:health f00d::ac14:0:0:cb43 172.20.0.92 ready
+```
+
+### 3.检查初始状态
+
+当然“死星”应该只允许“帝国”的飞船着陆,因为没有应用任何策略,所以初始状态下“帝国”和“联盟”的飞船都可以登陆,如下测试:
+
+``` bash
+$ kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
+Ship landed # 成功着陆
+$ kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
+Ship landed # 成功着陆
+```
+
+### 4.应用 L3/L4 策略
+
+现在我们应用策略,仅让带有标签 `org=empire`的飞船登陆“死星”;那么带有标签 `org=alliance`的“联盟”飞船将禁止登陆;这个就是我们熟悉的传统L3/L4 防火墙策略,并跟踪连接(会话)状态;
+
+
+
+根据文件[sw_l3_l4_policy.yaml](../roles/cilium/files/star_war_example/sw_l3_l4_policy.yaml) 创建 `$ kubectl apply -f sw_l3_l4_policy.yaml` 后,验证如下:
+
+``` bash
+$ kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
+Ship landed # 成功着陆
+
+$ kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
+# 失败超时
+```
+
+### 5.查看安全策略
+
+再次执行 `cilium endpoint list`,可以看到标签带`deathstar`的 POD 已经应用了 `Ingress`方向的策略:
+
+``` bash
+# kubectl exec -n kube-system cilium-6t5vx -- cilium endpoint list
+ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
+ ENFORCEMENT ENFORCEMENT
+643 Enabled Disabled 31371 k8s:class=deathstar f00d::ac14:0:0:283 172.20.0.246 ready
+ k8s:io.cilium.k8s.policy.serviceaccount=default
+ k8s:io.kubernetes.pod.namespace=default
+ k8s:org=empire
+1011 Enabled Disabled 31371 k8s:class=deathstar f00d::ac14:0:0:3f3 172.20.0.63 ready
+ k8s:io.cilium.k8s.policy.serviceaccount=default
+ k8s:io.kubernetes.pod.namespace=default
+ k8s:org=empire
+32030 Disabled Disabled 5350 k8s:class=tiefighter f00d::ac14:0:0:7d1e 172.20.0.201 ready
+ k8s:io.cilium.k8s.policy.serviceaccount=default
+ k8s:io.kubernetes.pod.namespace=default
+ k8s:org=empire
+45943 Disabled Disabled 14309 k8s:class=xwing f00d::ac14:0:0:b377 172.20.0.189 ready
+ k8s:io.cilium.k8s.policy.serviceaccount=default
+ k8s:io.kubernetes.pod.namespace=default
+ k8s:org=alliance
+52035 Disabled Disabled 4 reserved:health f00d::ac14:0:0:cb43 172.20.0.92 ready
+```
+
+查看具体策略内容 `kubectl describe cnp rule1`
+
+### 6. L7 安全策略
+
+上述的策略可以进行简单的安全防护了,但是“死星”的这个系统还有很多复杂的功能;比如它还提供了一个内部维护接口,如果被不合理调用将带来严重灾难性后果,也许“联盟”勇士劫持了一架“帝国”飞船正在进行这个任务(虽然我们内心希望他能够成功摧毁“死星”)。不幸的是“死星”系统设计者考虑到这个风险,它有办法严格限制每架飞船能够请求的权限。
+
+没有限制飞船请求权限时,如下运行:
+
+``` bash
+$ kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
+Panic: deathstar exploded
+
+goroutine 1 [running]:
+main.HandleGarbage(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
+ /code/src/github.com/empire/deathstar/
+ temp/main.go:9 +0x64
+main.main()
+ /code/src/github.com/empire/deathstar/
+ temp/main.go:5 +0x85
+```
+
+
+
+限制L7 的安全策略,根据文件[sw_l3_l4_l7_policy.yaml](../roles/cilium/files/star_war_example/sw_l3_l4_l7_policy.yaml) 创建 `$ kubectl apply -f sw_l3_l4_l7_policy.yaml` 后,验证如下:
+
+``` bash
+$ kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
+Ship landed
+$ kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
+Access denied
+```
+
+我们同样可以使用 `kubectl desribe cnp`检查更新的策略,或者使用 `cilium` 命令行:
+
+``` bash
+$ kubectl exec -n kube-system cilium-6t5vx -- cilium policy get
+[
+ {
+ "endpointSelector": {
+ "matchLabels": {
+ "any:class": "deathstar",
+ "any:org": "empire",
+ "k8s:io.kubernetes.pod.namespace": "default"
+ }
+ },
+ "ingress": [
+ {
+ "fromEndpoints": [
+ {
+ "matchLabels": {
+ "any:org": "empire",
+ "k8s:io.kubernetes.pod.namespace": "default"
+ }
+ }
+ ],
+ "toPorts": [
+ {
+ "ports": [
+ {
+ "port": "80",
+ "protocol": "TCP"
+ }
+ ],
+ "rules": {
+ "http": [
+ {
+ "path": "/v1/request-landing",
+ "method": "POST"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ],
+ "labels": [
+ {
+ "key": "io.cilium.k8s.policy.name",
+ "value": "rule1",
+ "source": "k8s"
+ },
+ {
+ "key": "io.cilium.k8s.policy.namespace",
+ "value": "default",
+ "source": "k8s"
+ }
+ ]
+ }
+]
+Revision: 267
+```
+我们看到 `cilium` 可以实现 `7层 HTTP `协议的请求方法(GET/PUT/POST等)、路径(/v1/request-landing)等等安全策略;另外,它还可以防护其他应用(如:Kafka, gRPC, Elasticsearch),可以去官网文档示例学习!
+
+## 参考资料
+
+- [cilium github](https://github.com/cilium/cilium)
+- [cilium doc](http://docs.cilium.io)
diff --git a/docs/setup/network-plugin/flannel.md b/docs/setup/network-plugin/flannel.md
new file mode 100644
index 0000000..4148e92
--- /dev/null
+++ b/docs/setup/network-plugin/flannel.md
@@ -0,0 +1,126 @@
+## 06-安装flannel网络组件.md
+
+本项目提供多种网络插件可选,如果需要安装flannel,请在/etc/ansible/hosts文件中设置变量 `CLUSTER_NETWORK="flannel"`,更多设置请查看`roles/flannel/defaults/main.yml`
+
+`Flannel`是最早应用到k8s集群的网络插件之一,简单高效,且提供多个后端`backend`模式供选择;本文介绍以`DaemonSet Pod`方式集成到k8s集群,需要在所有master节点和node节点安装。
+
+``` text
+roles/flannel/
+├── tasks
+│ └── main.yml
+└── templates
+ └── kube-flannel.yaml.j2
+```
+
+请在另外窗口打开[roles/flannel/tasks/main.yml](../roles/flannel/tasks/main.yml) 文件,对照看以下讲解内容。
+
+### 下载基础cni 插件
+
+请到CNI 插件最新[release](https://github.com/containernetworking/plugins/releases)页面下载[cni-v0.6.0.tgz](https://github.com/containernetworking/plugins/releases/download/v0.6.0/cni-v0.6.0.tgz),解压后里面有很多插件,选择如下几个复制到项目 `bin`目录下
+
+- flannel用到的插件
+ - bridge
+ - flannel
+ - host-local
+ - loopback
+ - portmap
+
+Flannel CNI 插件的配置文件可以包含多个`plugin` 或由其调用其他`plugin`;`Flannel DaemonSet Pod`运行以后会生成`/run/flannel/subnet.env `文件,例如:
+
+``` bash
+FLANNEL_NETWORK=10.1.0.0/16
+FLANNEL_SUBNET=10.1.17.1/24
+FLANNEL_MTU=1472
+FLANNEL_IPMASQ=true
+```
+然后它利用这个文件信息去配置和调用`bridge`插件来生成容器网络,调用`host-local`来管理`IP`地址,例如:
+
+``` bash
+{
+ "name": "mynet",
+ "type": "bridge",
+ "mtu": 1472,
+ "ipMasq": false,
+ "isGateway": true,
+ "ipam": {
+ "type": "host-local",
+ "subnet": "10.1.17.0/24"
+ }
+}
+```
+- 更多相关介绍请阅读:
+ - [flannel kubernetes 集成](https://github.com/coreos/flannel/blob/master/Documentation/kubernetes.md)
+ - [flannel cni 插件](https://github.com/containernetworking/plugins/tree/master/plugins/meta/flannel)
+ - [更多 cni 插件](https://github.com/containernetworking/plugins)
+
+### 准备`Flannel DaemonSet` yaml配置文件
+
+请阅读 `roles/flannel/templates/kube-flannel.yaml.j2` 内容,注意:
+
++ 本安装方式,flannel使用apiserver 存储数据
++ 配置相关RBAC 权限和 `service account`
++ 配置`ConfigMap`包含 CNI配置和 flannel配置(指定backend等),和`hosts`文件中相关设置对应
++ `DaemonSet Pod`包含两个容器,一个容器运行flannel本身,另一个init容器部署cni 配置文件
++ 为方便国内加速使用镜像 `jmgao1983/flannel:v0.10.0-amd64` (官方镜像在docker-hub上的转存)
++ 特别注意:如果服务器是多网卡(例如vagrant环境),则需要在`roles/flannel/templates/kube-flannel.yaml.j2 `中增加指定环境变量,详见 [kubernetes ISSUE 39701](https://github.com/kubernetes/kubernetes/issues/39701)
+
+``` bash
+ ...
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ - name: KUBERNETES_SERVICE_HOST # 指定apiserver的主机地址
+ value: {{ MASTER_IP }}
+ - name: KUBERNETES_SERVICE_PORT # 指定apiserver的服务端口
+ value: {{ KUBE_APISERVER.split(':')[2] }}
+ ...
+```
+### 安装 flannel网络
+
++ 安装之前必须确保kube-master和kube-node节点已经成功部署
++ 只需要在任意装有kubectl客户端的节点运行 kubectl create安装即可
++ 等待15s后(视网络拉取相关镜像速度),flannel 网络插件安装完成,删除之前kube-node安装时默认cni网络配置
+
+### 验证flannel网络
+
+执行flannel安装成功后可以验证如下:(需要等待镜像下载完成,有时候即便上一步已经配置了docker国内加速,还是可能比较慢,请确认以下容器运行起来以后,再执行后续验证步骤)
+
+``` bash
+# kubectl get pod --all-namespaces
+NAMESPACE NAME READY STATUS RESTARTS AGE
+kube-system kube-flannel-ds-m8mzm 1/1 Running 0 3m
+kube-system kube-flannel-ds-mnj6j 1/1 Running 0 3m
+kube-system kube-flannel-ds-mxn6k 1/1 Running 0 3m
+```
+在集群创建几个测试pod: `kubectl run test --image=busybox --replicas=3 sleep 30000`
+
+``` bash
+# kubectl get pod --all-namespaces -o wide|head -n 4
+NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE
+default busy-5956b54c8b-ld4gb 1/1 Running 0 9m 172.20.2.7 192.168.1.1
+default busy-5956b54c8b-lj9l9 1/1 Running 0 9m 172.20.1.5 192.168.1.2
+default busy-5956b54c8b-wwpkz 1/1 Running 0 9m 172.20.0.6 192.168.1.3
+
+# 查看路由
+# ip route
+default via 192.168.1.254 dev ens3 onlink
+192.168.1.0/24 dev ens3 proto kernel scope link src 192.168.1.1
+172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
+172.20.0.0/24 via 192.168.1.3 dev ens3
+172.20.1.0/24 via 192.168.1.2 dev ens3
+172.20.2.0/24 dev cni0 proto kernel scope link src 172.20.2.1
+```
+在各节点上分别 ping 这三个POD IP地址,确保能通:
+
+``` bash
+ping 172.20.2.7
+ping 172.20.1.5
+ping 172.20.0.6
+```
+
diff --git a/docs/setup/network-plugin/kube-router.md b/docs/setup/network-plugin/kube-router.md
new file mode 100644
index 0000000..6d28c1d
--- /dev/null
+++ b/docs/setup/network-plugin/kube-router.md
@@ -0,0 +1,107 @@
+# kube-router 网络组件
+
+kube-router是一个简单、高效的网络插件,它提供一揽子解决方案:
+- 基于GoBGP 提供Pod 网络互联(Routing)
+- 使用ipsets优化的iptables 提供网络策略支持(Firewall/NetworkPolicy)
+- 基于IPVS/LVS 提供高性能服务代理(Service Proxy)
+
+更多介绍请前往`https://github.com/cloudnativelabs/kube-router`
+
+## 配置
+
+本项目提供多种网络插件可选,如果需要安装kube-router,请在/etc/ansible/hosts文件中设置变量 `CLUSTER_NETWORK="kube-router"`,更多设置请查看`roles/kube-router/defaults/main.yml`
+
+- kube-router需要在所有master节点和node节点安装
+
+## 安装
+
+- 单步安装已经集成:`ansible-playbook 90.setup.yml`
+- 分步安装请执行:`ansible-playbook 06.network.yml`
+
+## 验证
+
+- 1.pod间网络联通性:略
+
+- 2.host路由表
+
+``` bash
+# master上路由
+root@master1:~$ ip route
+...
+172.20.1.0/24 via 192.168.1.2 dev ens3 proto 17
+172.20.2.0/24 via 192.168.1.3 dev ens3 proto 17
+...
+
+# node3上路由
+root@node3:~$ ip route
+...
+172.20.0.0/24 via 192.168.1.1 dev ens3 proto 17
+172.20.1.0/24 via 192.168.1.2 dev ens3 proto 17
+172.20.2.0/24 dev kube-bridge proto kernel scope link src 172.20.2.1
+...
+```
+
+- 3.bgp连接状态
+
+``` bash
+# master上
+root@master1:~$ netstat -antlp|grep router|grep LISH|grep 179
+tcp 0 0 192.168.1.1:179 192.168.1.3:58366 ESTABLISHED 26062/kube-router
+tcp 0 0 192.168.1.1:42537 192.168.1.2:179 ESTABLISHED 26062/kube-router
+
+# node3上
+root@node3:~$ netstat -antlp|grep router|grep LISH|grep 179
+tcp 0 0 192.168.1.3:58366 192.168.1.1:179 ESTABLISHED 18897/kube-router
+tcp 0 0 192.168.1.3:179 192.168.1.2:43928 ESTABLISHED 18897/kube-router
+
+```
+
+- 4.NetworkPolicy有效性,验证参照[这里](guide/networkpolicy.md)
+
+- 5.ipset列表查看
+
+``` bash
+$ ipset list
+...
+Name: kube-router-pod-subnets
+Type: hash:net
+Revision: 6
+Header: family inet hashsize 1024 maxelem 65536 timeout 0
+Size in memory: 672
+References: 2
+Members:
+172.20.1.0/24 timeout 0
+172.20.2.0/24 timeout 0
+172.20.0.0/24 timeout 0
+
+Name: kube-router-node-ips
+Type: hash:ip
+Revision: 4
+Header: family inet hashsize 1024 maxelem 65536 timeout 0
+Size in memory: 416
+References: 1
+Members:
+192.168.1.1 timeout 0
+192.168.1.2 timeout 0
+192.168.1.3 timeout 0
+...
+```
+
+- 6.ipvs虚拟服务器查看 (roles/kube-router/defaults/main.yml 需配置`SERVICE_PROXY: "true"`)
+
+``` bash
+# 首先创建测试应用
+$ kubectl run nginx --image=nginx --replicas=3 --port=80 --expose
+
+# 查看ipvsadm输出
+$ ipvsadm
+IP Virtual Server version 1.2.1 (size=4096)
+Prot LocalAddress:Port Scheduler Flags
+ -> RemoteAddress:Port Forward Weight ActiveConn InActConn
+TCP 10.68.0.1:https rr persistent 10800 # 这个kubernetes虚拟服务地址
+ -> 192.168.1.1:6443 Masq 1 0 0
+TCP 10.68.199.39:http rr # 这个是测试应用nginx的虚拟服务地址
+ -> 172.20.1.5:http Masq 1 0 0
+ -> 172.20.2.6:http Masq 1 0 0
+ -> 172.20.2.8:http Masq 1 0 0
+```
diff --git a/docs/setup/quickStart.md b/docs/setup/quickStart.md
new file mode 100644
index 0000000..c72e4e0
--- /dev/null
+++ b/docs/setup/quickStart.md
@@ -0,0 +1,147 @@
+## 快速指南
+
+以下为快速体验k8s集群的测试、开发环境--allinone部署,国内环境下觉得比官方的minikube方便、简单很多。
+
+### 1.基础系统配置
+
++ 推荐内存2G/硬盘30G以上
++ 最小化安装`Ubuntu 16.04 server`或者`CentOS 7 Minimal`
++ 配置基础网络、更新源、SSH登陆等
+
+### 2.安装依赖工具
+
+Ubuntu 16.04 请执行以下脚本:
+
+``` bash
+# 文档中脚本默认均以root用户执行
+# 安装依赖工具
+apt-get install python2.7 git python-pip
+# Ubuntu16.04可能需要配置以下软连接
+ln -s /usr/bin/python2.7 /usr/bin/python
+```
+CentOS 7 请执行以下脚本:
+
+``` bash
+# 文档中脚本默认均以root用户执行
+# 安装 epel 源
+yum install epel-release -y
+# 安装依赖工具
+yum install git python python-pip -y
+```
+### 3.ansible安装及准备
+
+``` bash
+# 安装ansible (国内如果安装太慢可以直接用pip阿里云加速)
+#pip install pip --upgrade
+#pip install ansible
+pip install pip --upgrade -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
+pip install --no-cache-dir ansible -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
+# 配置ansible ssh密钥登陆
+ssh-keygen -t rsa -b 2048 回车 回车 回车
+ssh-copy-id $IP #$IP为本虚机地址,按照提示输入yes 和root密码
+```
+
+在`Ubuntu 16.04`中,如果出现以下错误:
+
+``` bash
+Traceback (most recent call last):
+ File "/usr/bin/pip", line 9, in
+ from pip import main
+ImportError: cannot import name main
+```
+将`/usr/bin/pip`做以下修改:
+
+``` bash
+#原代码
+from pip import main
+if __name__ == '__main__':
+ sys.exit(main())
+
+#修改后
+from pip import __main__
+if __name__ == '__main__':
+ sys.exit(__main__._main())
+```
+
+### 4.安装kubernetes集群
+
+- 4.1 下载项目源码
+
+``` bash
+# 方式一:使用git clone
+git clone https://github.com/gjmzj/kubeasz.git
+mkdir -p /etc/ansible
+mv kubeasz/* /etc/ansible
+# 方式二:从发布页面 https://github.com/gjmzj/kubeasz/releases 下载源码解压到同样目录
+```
+- 4.2a 下载二进制文件
+请从分享的[百度云链接](https://pan.baidu.com/s/1c4RFaA),下载解压到/etc/ansible/bin目录,如果你有合适网络环境也可以按照/down/download.sh自行从官网下载各种tar包
+
+``` bash
+tar zxvf k8s.1-9-8.tar.gz # 以安装k8s v1.9.8为例
+mv bin/* /etc/ansible/bin
+```
+- 4.2b [可选]下载离线docker镜像
+服务器使用内部yum源/apt源,但是无法访问公网情况下,请下载离线docker镜像完成集群安装;从百度云盘把`basic_images_kubeasz_x.y.tar.gz` 下载解压到`/etc/ansible/down` 目录
+
+``` bash
+tar zxvf basic_images_kubeasz_0.2.tar.gz -C /etc/ansible/down
+```
+- 4.3 配置集群参数
+ - 4.3.1 必要配置:`cd /etc/ansible && cp example/hosts.allinone.example hosts`, 然后实际情况修改此hosts文件
+ - 4.3.2 可选配置,初次使用可以不做修改,详见[配置指南](config_guide.md)
+ - 4.3.3 验证ansible 安装:`ansible all -m ping` 正常能看到节点返回 SUCCESS
+
+- 4.4 开始安装
+如果你对集群安装流程不熟悉,请阅读项目首页 **安装步骤** 讲解后分步安装,并对 **每步都进行验证**
+
+``` bash
+# 分步安装
+ansible-playbook 01.prepare.yml
+ansible-playbook 02.etcd.yml
+ansible-playbook 03.docker.yml
+ansible-playbook 04.kube-master.yml
+ansible-playbook 05.kube-node.yml
+ansible-playbook 06.network.yml
+ansible-playbook 07.cluster-addon.yml
+# 一步安装
+#ansible-playbook 90.setup.yml
+```
+
++ [可选]对集群节点进行操作系统层面的安全加固 `ansible-playbook roles/os-harden/os-harden.yml`,详情请参考[os-harden项目](https://github.com/dev-sec/ansible-os-hardening)
+
+### 5.验证安装
+如果提示kubectl: command not found,退出重新ssh登陆一下,环境变量生效即可
+
+``` bash
+kubectl version
+kubectl get componentstatus # 可以看到scheduler/controller-manager/etcd等组件 Healthy
+kubectl cluster-info # 可以看到kubernetes master(apiserver)组件 running
+kubectl get node # 可以看到单 node Ready状态
+kubectl get pod --all-namespaces # 可以查看所有集群pod状态,默认已安装网络插件、coredns、metrics-server等
+kubectl get svc --all-namespaces # 可以查看所有集群服务状态
+```
+### 6.安装主要组件
+
+``` bash
+# 安装kubedns,默认已集成安装
+#kubectl create -f /etc/ansible/manifests/kubedns
+# 安装dashboard,默认已集成安装
+#kubectl create -f /etc/ansible/manifests/dashboard
+```
++ 登陆 `dashboard`可以查看和管理集群,更多内容请查阅[dashboard文档](../guide/dashboard.md)
+
+### 7.清理集群
+
+以上步骤创建的K8S开发测试环境请尽情折腾,碰到错误尽量通过查看日志、上网搜索、提交`issues`等方式解决;当然如果是彻底奔溃了,可以清理集群后重新创建。
+
+``` bash
+ansible-playbook 99.clean.yml
+```
+
+如果出现清理失败,类似报错:`... Device or resource busy: '/var/run/docker/netns/xxxxxxxxxx'`,需要手动umount该目录后清理
+
+``` bash
+$ umount /var/run/docker/netns/xxxxxxxxxx
+$ rm -rf /var/run/docker/netns/xxxxxxxxxx
+```
diff --git a/down/download.sh b/down/download.sh
new file mode 100644
index 0000000..1553080
--- /dev/null
+++ b/down/download.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+# This script describes where to download the official released binaries needed
+# It's suggested to download the entire *.tar.gz at https://pan.baidu.com/s/1c4RFaA
+
+# example releases
+K8S_VER=v1.10.4
+ETCD_VER=v3.3.8
+DOCKER_VER=17.03.2-ce
+CNI_VER=v0.6.0
+DOCKER_COMPOSE=1.18.0
+HARBOR=v1.5.2
+
+echo "\nNote1: Before this script, please finish downloading binaries manually from following urls."
+echo "\nNote2:If binaries are not ready, use `Ctrl + C` to stop this script."
+
+echo "\n----download k8s binary at:"
+echo https://dl.k8s.io/${K8S_VER}/kubernetes-server-linux-amd64.tar.gz
+
+echo "\n----download etcd binary at:"
+echo https://github.com/coreos/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz
+echo https://storage.googleapis.com/etcd/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz
+
+echo "\n----download docker binary at:"
+echo https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VER}.tgz
+
+echo "\n----download ca tools at:"
+echo https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
+echo https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
+echo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
+
+echo "\n----download docker-compose at:"
+echo https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE}/docker-compose-Linux-x86_64
+
+echo "\n----download harbor-offline-installer at:"
+echo https://github.com/vmware/harbor/releases/download/${HARBOR}/harbor-offline-installer-${HARBOR}.tgz
+
+echo "\n----download cni plugins at:"
+echo https://github.com/containernetworking/plugins/releases
+
+sleep 30
+
+### prepare 'cfssl' cert tool suit
+echo "\nMoving 'cfssl' to 'bin' dir..."
+if [ -f "cfssl_linux-amd64" ]; then
+ mv -f cfssl_linux-amd64 ../bin/cfssl
+else
+ echo Please download 'cfssl' at 'https://pkg.cfssl.org/R1.2/cfssl_linux-amd64'
+fi
+if [ -f "cfssljson_linux-amd64" ]; then
+ mv -f cfssljson_linux-amd64 ../bin/cfssljson
+else
+ echo Please download 'cfssljson' at 'https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64'
+fi
+if [ -f "cfssl-certinfo_linux-amd64" ]; then
+ mv -f cfssl-certinfo_linux-amd64 ../bin/cfssl-certinfo
+else
+ echo Please download 'cfssl-certinfo' at 'https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64'
+fi
+
+### prepare 'etcd' binaries
+if [ -f "etcd-${ETCD_VER}-linux-amd64.tar.gz" ]; then
+ echo "\nextracting etcd binaries..."
+ tar zxf etcd-${ETCD_VER}-linux-amd64.tar.gz
+ mv -f etcd-${ETCD_VER}-linux-amd64/etcd* ../bin
+else
+ echo Please download 'etcd-${ETCD_VER}-linux-amd64.tar.gz' first
+fi
+
+### prepare kubernetes binaries
+if [ -f "kubernetes-server-linux-amd64.tar.gz" ]; then
+ echo "\nextracting kubernetes binaries..."
+ tar zxf kubernetes-server-linux-amd64.tar.gz
+ mv -f kubernetes/server/bin/kube-apiserver ../bin
+ mv -f kubernetes/server/bin/kube-controller-manager ../bin
+ mv -f kubernetes/server/bin/kubectl ../bin
+ mv -f kubernetes/server/bin/kubelet ../bin
+ mv -f kubernetes/server/bin/kube-proxy ../bin
+ mv -f kubernetes/server/bin/kube-scheduler ../bin
+else
+ echo Please download 'kubernetes-server-linux-amd64.tar.gz' first
+fi
+
+### prepare docker binaries
+if [ -f "docker-${DOCKER_VER}.tgz" ]; then
+ echo "\nextracting docker binaries..."
+ tar zxf docker-${DOCKER_VER}.tgz
+ mv -f docker/docker* ../bin
+ if [ -f "docker/completion/bash/docker" ]; then
+ mv -f docker/completion/bash/docker ../roles/docker/files/docker
+ fi
+else
+ echo Please download 'docker-${DOCKER_VER}.tgz' first
+fi
+
+### prepare cni plugins, needed by flannel;
+if [ -f "cni-${CNI_VER}.tgz" ]; then
+ echo "\nextracting cni plugins binaries..."
+ tar zxf cni-${CNI_VER}.tgz
+ mv -f bridge ../bin
+ mv -f flannel ../bin
+ mv -f host-local ../bin
+ mv -f loopback ../bin
+ mv -f portmap ../bin
+else
+ echo Please download 'cni-${CNI_VER}.tgz' first
+fi
diff --git a/down/offline_images b/down/offline_images
new file mode 100644
index 0000000..c2d5ae6
--- /dev/null
+++ b/down/offline_images
@@ -0,0 +1,50 @@
+###---[basic_images ]
+# dns-addon
+coredns/coredns:1.2.6
+mirrorgooglecontainers/k8s-dns-kube-dns-amd64:1.14.13
+mirrorgooglecontainers/k8s-dns-dnsmasq-nanny-amd64:1.14.13
+mirrorgooglecontainers/k8s-dns-sidecar-amd64:1.14.13
+# metrics-server
+mirrorgooglecontainers/metrics-server-amd64:v0.3.1
+# calico
+calico/node:v3.4.1
+calico/cni:v3.4.1
+calico/kube-controllers:v3.4.1
+# cilium
+cilium/cilium:v1.1.4
+# flannel
+jmgao1983/flannel:v0.11.0-amd64
+# kube-router
+cloudnativelabs/kube-router:v0.2.0
+# dashboard
+mirrorgooglecontainers/kubernetes-dashboard-amd64:v1.10.0
+# pause
+mirrorgooglecontainers/pause-amd64:3.1
+busybox:1.28.4
+# traefik ingress
+traefik:v1.7.4
+# heapster
+mirrorgooglecontainers/heapster-grafana-amd64:v4.4.3
+mirrorgooglecontainers/heapster-amd64:v1.5.4
+mirrorgooglecontainers/heapster-influxdb-amd64:v1.3.3
+
+###---[extra-images]
+# helm tiller server
+jmgao1983/tiller:v2.12.3
+# efk
+mirrorgooglecontainers/elasticsearch:v5.6.4
+alpine:3.6
+mirrorgooglecontainers/fluentd-elasticsearch:v2.0.2
+jmgao1983/kibana:5.6.4
+# nfs dynamic PV provisioner
+jmgao1983/nfs-client-provisioner:latest
+# prometheus
+busybox:latest
+grafana/grafana:5.1.2
+mirrorgooglecontainers/kube-state-metrics:v1.3.1
+appropriate/curl:latest
+prom/prometheus:v2.2.1
+prom/alertmanager:v0.14.0
+prom/node-exporter:v0.15.2
+jimmidyson/configmap-reload:v0.1
+
diff --git a/example/hosts.allinone.example b/example/hosts.allinone.example
new file mode 100644
index 0000000..35249d2
--- /dev/null
+++ b/example/hosts.allinone.example
@@ -0,0 +1,68 @@
+# 集群部署节点:一般为运行ansible 脚本的节点
+# 变量 NTP_ENABLED (=yes/no) 设置集群是否安装 chrony 时间同步
+[deploy]
+192.168.1.1 NTP_ENABLED=no
+
+# etcd集群请提供如下NODE_NAME,注意etcd集群必须是1,3,5,7...奇数个节点
+[etcd]
+192.168.1.1 NODE_NAME=etcd1
+
+[kube-master]
+192.168.1.1
+
+[kube-node]
+192.168.1.1
+
+# 参数 NEW_INSTALL:yes表示新建,no表示使用已有harbor服务器
+# 如果不使用域名,可以设置 HARBOR_DOMAIN=""
+[harbor]
+#192.168.1.8 HARBOR_DOMAIN="harbor.yourdomain.com" NEW_INSTALL=no
+
+# 【可选】外部负载均衡,用于自有环境负载转发 NodePort 暴露的服务等
+[ex-lb]
+#192.168.1.6 LB_ROLE=backup EX_VIP=192.168.1.250
+#192.168.1.7 LB_ROLE=master EX_VIP=192.168.1.250
+
+[all:vars]
+# ---------集群主要参数---------------
+#集群部署模式:allinone, single-master, multi-master
+DEPLOY_MODE=allinone
+
+#集群 MASTER IP,自动生成
+MASTER_IP="{{ groups['kube-master'][0] }}"
+KUBE_APISERVER="https://{{ MASTER_IP }}:6443"
+
+# 集群网络插件,目前支持calico, flannel, kube-router, cilium
+CLUSTER_NETWORK="flannel"
+
+# 服务网段 (Service CIDR),注意不要与内网已有网段冲突
+SERVICE_CIDR="10.68.0.0/16"
+
+# POD 网段 (Cluster CIDR),注意不要与内网已有网段冲突
+CLUSTER_CIDR="172.20.0.0/16"
+
+# 服务端口范围 (NodePort Range)
+NODE_PORT_RANGE="20000-40000"
+
+# kubernetes 服务 IP (预分配,一般是 SERVICE_CIDR 中第一个IP)
+CLUSTER_KUBERNETES_SVC_IP="10.68.0.1"
+
+# 集群 DNS 服务 IP (从 SERVICE_CIDR 中预分配)
+CLUSTER_DNS_SVC_IP="10.68.0.2"
+
+# 集群 DNS 域名
+CLUSTER_DNS_DOMAIN="cluster.local."
+
+# 集群basic auth 使用的用户名和密码 (运行时会生成随机密码)
+BASIC_AUTH_USER="admin"
+BASIC_AUTH_PASS="test1234"
+
+# ---------附加参数--------------------
+#默认二进制文件目录
+bin_dir="/opt/kube/bin"
+
+#证书目录
+ca_dir="/etc/kubernetes/ssl"
+
+#部署目录,即 ansible 工作目录
+base_dir="/etc/ansible"
diff --git a/example/hosts.allinone.example.en b/example/hosts.allinone.example.en
new file mode 100644
index 0000000..157d263
--- /dev/null
+++ b/example/hosts.allinone.example.en
@@ -0,0 +1,69 @@
+# 'deploy' node, which the ansible-playbooks usually run on
+# variable 'NTP_ENABLED(=yes/no)' enables/disables the NTP server 'chrony'
+[deploy]
+192.168.1.1 NTP_ENABLED=no
+
+# 'etcd' cluster must have odd member(s) (1,3,5,...)
+# variable 'NODE_NAME' is the distinct name of a member in 'etcd' cluster
+[etcd]
+192.168.1.1 NODE_NAME=etcd1
+
+[kube-master]
+192.168.1.1
+
+[kube-node]
+192.168.1.1
+
+# set 'NEW_INSTALL': 'yes' to install a harbor server; 'no' to integrate with existed one
+# if not using domian,set 'HARBOR_DOMAIN=""'
+[harbor]
+#192.168.1.8 HARBOR_DOMAIN="harbor.yourdomain.com" NEW_INSTALL=no
+
+# [optional] loadbalance for services with type 'NodePort'
+[ex-lb]
+#192.168.1.6 LB_ROLE=backup EX_VIP=192.168.1.250
+#192.168.1.7 LB_ROLE=master EX_VIP=192.168.1.250
+
+[all:vars]
+# --------- Main Variables ---------------
+# Cluster Deployment Mode: allinone, single-master, multi-master
+DEPLOY_MODE=allinone
+
+# Cluster's Master IP, auto generated
+MASTER_IP="{{ groups['kube-master'][0] }}"
+KUBE_APISERVER="https://{{ MASTER_IP }}:6443"
+
+# Network plugins supported: calico, flannel, kube-router, cilium
+CLUSTER_NETWORK="flannel"
+
+# K8S Service CIDR, not overlap with node(host) networking
+SERVICE_CIDR="10.68.0.0/16"
+
+# Cluster CIDR (Pod CIDR), not overlap with node(host) networking
+CLUSTER_CIDR="172.20.0.0/16"
+
+# NodePort Range
+NODE_PORT_RANGE="20000-40000"
+
+# Kubernetes SVC IP (usually assigned with the first available IP of 'SERVICE_CIDR')
+CLUSTER_KUBERNETES_SVC_IP="10.68.0.1"
+
+# Cluster DNS Server's IP (assigned with an available IP of 'SERVICE_CIDR')
+CLUSTER_DNS_SVC_IP="10.68.0.2"
+
+# Cluster DNS Domain
+CLUSTER_DNS_DOMAIN="cluster.local."
+
+# Basic auth for apiserver (a random password will be gennerated on cluster setup)
+BASIC_AUTH_USER="admin"
+BASIC_AUTH_PASS="test1234"
+
+# -------- Additional Variables --------------------
+# Binaries Directory
+bin_dir="/opt/kube/bin"
+
+# CA and other components cert/key Directory
+ca_dir="/etc/kubernetes/ssl"
+
+# Deploy Directory (kubeasz workspace), don't change the default value right now
+base_dir="/etc/ansible"
diff --git a/example/hosts.cloud.example b/example/hosts.cloud.example
new file mode 100644
index 0000000..5a7804c
--- /dev/null
+++ b/example/hosts.cloud.example
@@ -0,0 +1,68 @@
+# 集群部署节点:一般为运行ansible 脚本的节点
+# 变量 NTP_ENABLED (=yes/no) 设置集群是否安装 chrony 时间同步, 公有云上虚机不需要
+[deploy]
+192.168.1.1 NTP_ENABLED=no
+
+# etcd集群请提供如下NODE_NAME,注意etcd集群必须是1,3,5,7...奇数个节点
+[etcd]
+192.168.1.1 NODE_NAME=etcd1
+192.168.1.2 NODE_NAME=etcd2
+192.168.1.3 NODE_NAME=etcd3
+
+[kube-master]
+192.168.1.2
+192.168.1.3
+
+[kube-node]
+192.168.1.1
+192.168.1.4
+
+# 参数 NEW_INSTALL:yes表示新建,no表示使用已有harbor服务器
+# 如果不使用域名,可以设置 HARBOR_DOMAIN=""
+[harbor]
+#192.168.1.8 HARBOR_DOMAIN="harbor.yourdomain.com" NEW_INSTALL=no
+
+[all:vars]
+# ---------集群主要参数---------------
+#集群部署模式:allinone, single-master, multi-master
+DEPLOY_MODE=multi-master
+
+# 公有云上一般都有提供负载均衡产品,且不允许自己创建lb
+# 创建公有云内网负载均衡,然后配置:前端监听 tcp 8443,后端 tcp 6443,后端节点即 master 节点
+MASTER_IP="192.168.1.10" # 即负载均衡内网地址
+KUBE_APISERVER="https://{{ MASTER_IP }}:8443"
+
+# 集群网络插件,目前支持calico, flannel, kube-router, cilium
+CLUSTER_NETWORK="flannel"
+
+# 服务网段 (Service CIDR),注意不要与内网已有网段冲突
+SERVICE_CIDR="10.68.0.0/16"
+
+# POD 网段 (Cluster CIDR),注意不要与内网已有网段冲突
+CLUSTER_CIDR="172.20.0.0/16"
+
+# 服务端口范围 (NodePort Range)
+NODE_PORT_RANGE="20000-40000"
+
+# kubernetes 服务 IP (预分配,一般是 SERVICE_CIDR 中第一个IP)
+CLUSTER_KUBERNETES_SVC_IP="10.68.0.1"
+
+# 集群 DNS 服务 IP (从 SERVICE_CIDR 中预分配)
+CLUSTER_DNS_SVC_IP="10.68.0.2"
+
+# 集群 DNS 域名
+CLUSTER_DNS_DOMAIN="cluster.local."
+
+# 集群basic auth 使用的用户名和密码 (运行时会生成随机密码)
+BASIC_AUTH_USER="admin"
+BASIC_AUTH_PASS="test1234"
+
+# ---------附加参数--------------------
+#默认二进制文件目录
+bin_dir="/opt/kube/bin"
+
+#证书目录
+ca_dir="/etc/kubernetes/ssl"
+
+#部署目录,即 ansible 工作目录,建议不要修改
+base_dir="/etc/ansible"
diff --git a/example/hosts.cloud.example.en b/example/hosts.cloud.example.en
new file mode 100644
index 0000000..67fa159
--- /dev/null
+++ b/example/hosts.cloud.example.en
@@ -0,0 +1,69 @@
+# 'deploy' node, which the ansible-playbooks usually run on
+# variable 'NTP_ENABLED(=yes/no)' enables/disables the NTP server 'chrony'
+[deploy]
+192.168.1.1 NTP_ENABLED=no
+
+# 'etcd' cluster must have odd member(s) (1,3,5,...)
+# variable 'NODE_NAME' is the distinct name of a member in 'etcd' cluster
+[etcd]
+192.168.1.1 NODE_NAME=etcd1
+192.168.1.2 NODE_NAME=etcd2
+192.168.1.3 NODE_NAME=etcd3
+
+[kube-master]
+192.168.1.2
+192.168.1.3
+
+[kube-node]
+192.168.1.1
+192.168.1.4
+
+# set 'NEW_INSTALL': 'yes' to install a harbor server; 'no' to integrate with existed one
+# if not using domian,set 'HARBOR_DOMAIN=""'
+[harbor]
+#192.168.1.8 HARBOR_DOMAIN="harbor.yourdomain.com" NEW_INSTALL=no
+
+[all:vars]
+# --------- Main Variables ---------------
+# Cluster Deployment Mode: allinone, single-master, multi-master
+DEPLOY_MODE=multi-master
+
+# use loadballance service by the Cloud Providor, no 'lb' nodes needed
+# cloud loadballance service: listen on tcp 8443, with kube-masters(apiservers) as backend servers
+MASTER_IP="192.168.1.10"
+KUBE_APISERVER="https://{{ MASTER_IP }}:8443"
+
+# Network plugins supported: calico, flannel
+CLUSTER_NETWORK="flannel"
+
+# K8S Service CIDR, not overlap with node(host) networking
+SERVICE_CIDR="10.68.0.0/16"
+
+# Cluster CIDR (Pod CIDR), not overlap with node(host) networking
+CLUSTER_CIDR="172.20.0.0/16"
+
+# NodePort Range
+NODE_PORT_RANGE="20000-40000"
+
+# Kubernetes SVC IP (usually assigned with the first available IP of 'SERVICE_CIDR')
+CLUSTER_KUBERNETES_SVC_IP="10.68.0.1"
+
+# Cluster DNS Server's IP (assigned with an available IP of 'SERVICE_CIDR')
+CLUSTER_DNS_SVC_IP="10.68.0.2"
+
+# Cluster DNS Domain
+CLUSTER_DNS_DOMAIN="cluster.local."
+
+# Basic auth for apiserver (a random password will be gennerated on cluster setup)
+BASIC_AUTH_USER="admin"
+BASIC_AUTH_PASS="test1234"
+
+# -------- Additional Variables --------------------
+# Binaries Directory
+bin_dir="/opt/kube/bin"
+
+# CA and other components cert/key Directory
+ca_dir="/etc/kubernetes/ssl"
+
+# Deploy Directory (kubeasz workspace), don't change the default value right now
+base_dir="/etc/ansible"
diff --git a/example/hosts.m-masters.example b/example/hosts.m-masters.example
new file mode 100644
index 0000000..fde5b4f
--- /dev/null
+++ b/example/hosts.m-masters.example
@@ -0,0 +1,78 @@
+# 集群部署节点:一般为运行ansible 脚本的节点
+# 变量 NTP_ENABLED (=yes/no) 设置集群是否安装 chrony 时间同步
+[deploy]
+192.168.1.1 NTP_ENABLED=no
+
+# etcd集群请提供如下NODE_NAME,注意etcd集群必须是1,3,5,7...奇数个节点
+[etcd]
+192.168.1.1 NODE_NAME=etcd1
+192.168.1.2 NODE_NAME=etcd2
+192.168.1.3 NODE_NAME=etcd3
+
+[kube-master]
+192.168.1.1
+192.168.1.2
+
+[kube-node]
+192.168.1.3
+192.168.1.4
+
+# 参数 NEW_INSTALL:yes表示新建,no表示使用已有harbor服务器
+# 如果不使用域名,可以设置 HARBOR_DOMAIN=""
+[harbor]
+#192.168.1.8 HARBOR_DOMAIN="harbor.yourdomain.com" NEW_INSTALL=no
+
+# 负载均衡(目前已支持多于2节点,一般2节点就够了) 安装 haproxy+keepalived
+[lb]
+192.168.1.1 LB_ROLE=backup
+192.168.1.2 LB_ROLE=master
+
+#【可选】外部负载均衡,用于自有环境负载转发 NodePort 暴露的服务等
+[ex-lb]
+#192.168.1.6 LB_ROLE=backup EX_VIP=192.168.1.250
+#192.168.1.7 LB_ROLE=master EX_VIP=192.168.1.250
+
+[all:vars]
+# ---------集群主要参数---------------
+#集群部署模式:allinone, single-master, multi-master
+DEPLOY_MODE=multi-master
+
+# 集群 MASTER IP即 LB节点VIP地址,为区别与默认apiserver端口,设置VIP监听的服务端口8443
+# 公有云上请使用云负载均衡内网地址和监听端口
+MASTER_IP="192.168.1.10"
+KUBE_APISERVER="https://{{ MASTER_IP }}:8443"
+
+# 集群网络插件,目前支持calico, flannel, kube-router, cilium
+CLUSTER_NETWORK="flannel"
+
+# 服务网段 (Service CIDR),注意不要与内网已有网段冲突
+SERVICE_CIDR="10.68.0.0/16"
+
+# POD 网段 (Cluster CIDR),注意不要与内网已有网段冲突
+CLUSTER_CIDR="172.20.0.0/16"
+
+# 服务端口范围 (NodePort Range)
+NODE_PORT_RANGE="20000-40000"
+
+# kubernetes 服务 IP (预分配,一般是 SERVICE_CIDR 中第一个IP)
+CLUSTER_KUBERNETES_SVC_IP="10.68.0.1"
+
+# 集群 DNS 服务 IP (从 SERVICE_CIDR 中预分配)
+CLUSTER_DNS_SVC_IP="10.68.0.2"
+
+# 集群 DNS 域名
+CLUSTER_DNS_DOMAIN="cluster.local."
+
+# 集群basic auth 使用的用户名和密码 (运行时会生成随机密码)
+BASIC_AUTH_USER="admin"
+BASIC_AUTH_PASS="test1234"
+
+# ---------附加参数--------------------
+#默认二进制文件目录
+bin_dir="/opt/kube/bin"
+
+#证书目录
+ca_dir="/etc/kubernetes/ssl"
+
+#部署目录,即 ansible 工作目录,建议不要修改
+base_dir="/etc/ansible"
diff --git a/example/hosts.m-masters.example.en b/example/hosts.m-masters.example.en
new file mode 100644
index 0000000..8aab239
--- /dev/null
+++ b/example/hosts.m-masters.example.en
@@ -0,0 +1,79 @@
+# 'deploy' node, which the ansible-playbooks usually run on
+# variable 'NTP_ENABLED(=yes/no)' enables/disables the NTP server 'chrony'
+[deploy]
+192.168.1.1 NTP_ENABLED=no
+
+# 'etcd' cluster must have odd member(s) (1,3,5,...)
+# variable 'NODE_NAME' is the distinct name of a member in 'etcd' cluster
+[etcd]
+192.168.1.1 NODE_NAME=etcd1
+192.168.1.2 NODE_NAME=etcd2
+192.168.1.3 NODE_NAME=etcd3
+
+[kube-master]
+192.168.1.1
+192.168.1.2
+
+[kube-node]
+192.168.1.3
+192.168.1.4
+
+# set 'NEW_INSTALL': 'yes' to install a harbor server; 'no' to integrate with existed one
+# if not using domian,set 'HARBOR_DOMAIN=""'
+[harbor]
+#192.168.1.8 HARBOR_DOMAIN="harbor.yourdomain.com" NEW_INSTALL=no
+
+# 'loadbalance' node, with 'haproxy+keepalived' installed
+[lb]
+192.168.1.1 LB_ROLE=backup
+192.168.1.2 LB_ROLE=master
+
+# [optional] loadbalance for services with type 'NodePort'
+[ex-lb]
+#192.168.1.6 LB_ROLE=backup EX_VIP=192.168.1.250
+#192.168.1.7 LB_ROLE=master EX_VIP=192.168.1.250
+
+[all:vars]
+# --------- Main Variables ---------------
+# Cluster Deployment Mode: allinone, single-master, multi-master
+DEPLOY_MODE=multi-master
+
+# Cluster's Master IP, generated by 'keepalived' daemon on a 'lb' node here
+# 'haproxy' daemon listens on port 8443, directs requests to real apiservers on port 6443
+MASTER_IP="192.168.1.10"
+KUBE_APISERVER="https://{{ MASTER_IP }}:8443"
+
+# Network plugins supported: calico, flannel, kube-router, cilium
+CLUSTER_NETWORK="flannel"
+
+# K8S Service CIDR, not overlap with node(host) networking
+SERVICE_CIDR="10.68.0.0/16"
+
+# Cluster CIDR (Pod CIDR), not overlap with node(host) networking
+CLUSTER_CIDR="172.20.0.0/16"
+
+# NodePort Range
+NODE_PORT_RANGE="20000-40000"
+
+# Kubernetes SVC IP (usually assigned with the first available IP of 'SERVICE_CIDR')
+CLUSTER_KUBERNETES_SVC_IP="10.68.0.1"
+
+# Cluster DNS Server's IP (assigned with an available IP of 'SERVICE_CIDR')
+CLUSTER_DNS_SVC_IP="10.68.0.2"
+
+# Cluster DNS Domain
+CLUSTER_DNS_DOMAIN="cluster.local."
+
+# Basic auth for apiserver (a random password will be gennerated on cluster setup)
+BASIC_AUTH_USER="admin"
+BASIC_AUTH_PASS="test1234"
+
+# -------- Additional Variables --------------------
+# Binaries Directory
+bin_dir="/opt/kube/bin"
+
+# CA and other components cert/key Directory
+ca_dir="/etc/kubernetes/ssl"
+
+# Deploy Directory (kubeasz workspace), don't change the default value right now
+base_dir="/etc/ansible"
diff --git a/example/hosts.s-master.example b/example/hosts.s-master.example
new file mode 100644
index 0000000..610a9b3
--- /dev/null
+++ b/example/hosts.s-master.example
@@ -0,0 +1,69 @@
+# 集群部署节点:一般为运行ansible 脚本的节点
+# 变量 NTP_ENABLED (=yes/no) 设置集群是否安装 chrony 时间同步
+[deploy]
+192.168.1.1 NTP_ENABLED=no
+
+# etcd集群请提供如下NODE_NAME,请注意etcd集群必须是1,3,5,7...奇数个节点
+[etcd]
+192.168.1.1 NODE_NAME=etcd1
+
+[kube-master]
+192.168.1.1
+
+[kube-node]
+192.168.1.2
+192.168.1.3
+
+# 参数 NEW_INSTALL:yes表示新建,no表示使用已有harbor服务器
+# 如果不使用域名,可以设置 HARBOR_DOMAIN=""
+[harbor]
+#192.168.1.8 HARBOR_DOMAIN="harbor.yourdomain.com" NEW_INSTALL=no
+
+#【可选】外部负载均衡,用于自有环境负载转发 NodePort 暴露的服务等
+[ex-lb]
+#192.168.1.6 LB_ROLE=backup EX_VIP=192.168.1.250
+#192.168.1.7 LB_ROLE=master EX_VIP=192.168.1.250
+
+[all:vars]
+# ---------集群主要参数---------------
+#集群部署模式:allinone, single-master, multi-master
+DEPLOY_MODE=single-master
+
+#集群 MASTER IP,自动生成
+MASTER_IP="{{ groups['kube-master'][0] }}"
+KUBE_APISERVER="https://{{ MASTER_IP }}:6443"
+
+# 集群网络插件,目前支持calico, flannel, kube-router, cilium
+CLUSTER_NETWORK="flannel"
+
+# 服务网段 (Service CIDR),注意不要与内网已有网段冲突
+SERVICE_CIDR="10.68.0.0/16"
+
+# POD 网段 (Cluster CIDR),注意不要与内网已有网段冲突
+CLUSTER_CIDR="172.20.0.0/16"
+
+# 服务端口范围 (NodePort Range)
+NODE_PORT_RANGE="20000-40000"
+
+# kubernetes 服务 IP (预分配,一般是 SERVICE_CIDR 中第一个IP)
+CLUSTER_KUBERNETES_SVC_IP="10.68.0.1"
+
+# 集群 DNS 服务 IP (从 SERVICE_CIDR 中预分配)
+CLUSTER_DNS_SVC_IP="10.68.0.2"
+
+# 集群 DNS 域名
+CLUSTER_DNS_DOMAIN="cluster.local."
+
+# 集群basic auth 使用的用户名和密码 (运行时会生成随机密码)
+BASIC_AUTH_USER="admin"
+BASIC_AUTH_PASS="test1234"
+
+# ---------附加参数--------------------
+#默认二进制文件目录
+bin_dir="/opt/kube/bin"
+
+#证书目录
+ca_dir="/etc/kubernetes/ssl"
+
+#部署目录,即 ansible 工作目录
+base_dir="/etc/ansible"
diff --git a/example/hosts.s-master.example.en b/example/hosts.s-master.example.en
new file mode 100644
index 0000000..d03b9b1
--- /dev/null
+++ b/example/hosts.s-master.example.en
@@ -0,0 +1,70 @@
+# 'deploy' node, which the ansible-playbooks usually run on
+# variable 'NTP_ENABLED(=yes/no)' enables/disables the NTP server 'chrony'
+[deploy]
+192.168.1.1 NTP_ENABLED=no
+
+# 'etcd' cluster must have odd member(s) (1,3,5,...)
+# variable 'NODE_NAME' is the distinct name of a member in 'etcd' cluster
+[etcd]
+192.168.1.1 NODE_NAME=etcd1
+
+[kube-master]
+192.168.1.1
+
+[kube-node]
+192.168.1.2
+192.168.1.3
+
+# set 'NEW_INSTALL': 'yes' to install a harbor server; 'no' to integrate with existed one
+# if not using domian,set 'HARBOR_DOMAIN=""'
+[harbor]
+#192.168.1.8 HARBOR_DOMAIN="harbor.yourdomain.com" NEW_INSTALL=no
+
+# [optional] loadbalance for services with type 'NodePort'
+[ex-lb]
+#192.168.1.6 LB_ROLE=backup EX_VIP=192.168.1.250
+#192.168.1.7 LB_ROLE=master EX_VIP=192.168.1.250
+
+[all:vars]
+# --------- Main Variables ---------------
+# Cluster Deployment Mode: allinone, single-master, multi-master
+DEPLOY_MODE=single-master
+
+# Cluster's Master IP, auto generated
+MASTER_IP="{{ groups['kube-master'][0] }}"
+KUBE_APISERVER="https://{{ MASTER_IP }}:6443"
+
+# Network plugins supported: calico, flannel, kube-router, cilium
+CLUSTER_NETWORK="flannel"
+
+# K8S Service CIDR, not overlap with node(host) networking
+SERVICE_CIDR="10.68.0.0/16"
+
+# Cluster CIDR (Pod CIDR), not overlap with node(host) networking
+CLUSTER_CIDR="172.20.0.0/16"
+
+# NodePort Range
+NODE_PORT_RANGE="20000-40000"
+
+# Kubernetes SVC IP (usually assigned with the first available IP of 'SERVICE_CIDR')
+CLUSTER_KUBERNETES_SVC_IP="10.68.0.1"
+
+# Cluster DNS Server's IP (assigned with an available IP of 'SERVICE_CIDR')
+CLUSTER_DNS_SVC_IP="10.68.0.2"
+
+# Cluster DNS Domain
+CLUSTER_DNS_DOMAIN="cluster.local."
+
+# Basic auth for apiserver (a random password will be gennerated on cluster setup)
+BASIC_AUTH_USER="admin"
+BASIC_AUTH_PASS="test1234"
+
+# -------- Additional Variables --------------------
+# Binaries Directory
+bin_dir="/opt/kube/bin"
+
+# CA and other components cert/key Directory
+ca_dir="/etc/kubernetes/ssl"
+
+# Deploy Directory (kubeasz workspace), don't change the default value right now
+base_dir="/etc/ansible"
diff --git a/manifests/dashboard/1.6.3/kubernetes-dashboard.yaml b/manifests/dashboard/1.6.3/kubernetes-dashboard.yaml
new file mode 100644
index 0000000..ba21596
--- /dev/null
+++ b/manifests/dashboard/1.6.3/kubernetes-dashboard.yaml
@@ -0,0 +1,100 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Configuration to deploy release version of the Dashboard UI compatible with
+# Kubernetes 1.6 (RBAC enabled).
+#
+# Example usage: kubectl create -f
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ k8s-app: kubernetes-dashboard
+ name: kubernetes-dashboard
+ namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: kubernetes-dashboard
+ labels:
+ k8s-app: kubernetes-dashboard
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+subjects:
+- kind: ServiceAccount
+ name: kubernetes-dashboard
+ namespace: kube-system
+---
+kind: Deployment
+apiVersion: apps/v1beta1
+metadata:
+ labels:
+ k8s-app: kubernetes-dashboard
+ name: kubernetes-dashboard
+ namespace: kube-system
+spec:
+ replicas: 1
+ revisionHistoryLimit: 10
+ selector:
+ matchLabels:
+ k8s-app: kubernetes-dashboard
+ template:
+ metadata:
+ labels:
+ k8s-app: kubernetes-dashboard
+ spec:
+ containers:
+ - name: kubernetes-dashboard
+ #image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.6.3
+ image: mirrorgooglecontainers/kubernetes-dashboard-amd64:v1.6.3
+ ports:
+ - containerPort: 9090
+ protocol: TCP
+ args:
+ # Uncomment the following line to manually specify Kubernetes API server Host
+ # If not specified, Dashboard will attempt to auto discover the API server and connect
+ # to it. Uncomment only if the default does not work.
+ # - --apiserver-host=http://my-address:port
+ livenessProbe:
+ httpGet:
+ path: /
+ port: 9090
+ initialDelaySeconds: 30
+ timeoutSeconds: 30
+ serviceAccountName: kubernetes-dashboard
+ # Comment the following tolerations if Dashboard must not be deployed on master
+ tolerations:
+ - key: node-role.kubernetes.io/master
+ effect: NoSchedule
+---
+kind: Service
+apiVersion: v1
+metadata:
+ labels:
+ k8s-app: kubernetes-dashboard
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+ name: kubernetes-dashboard
+ namespace: kube-system
+spec:
+ ports:
+ - port: 80
+ targetPort: 9090
+ selector:
+ k8s-app: kubernetes-dashboard
+ type: NodePort
diff --git a/manifests/dashboard/admin-user-sa-rbac.yaml b/manifests/dashboard/admin-user-sa-rbac.yaml
new file mode 100644
index 0000000..667de88
--- /dev/null
+++ b/manifests/dashboard/admin-user-sa-rbac.yaml
@@ -0,0 +1,20 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: admin-user
+ namespace: kube-system
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: admin-user
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+subjects:
+- kind: ServiceAccount
+ name: admin-user
+ namespace: kube-system
+
diff --git a/manifests/dashboard/kubernetes-dashboard.yaml b/manifests/dashboard/kubernetes-dashboard.yaml
new file mode 100644
index 0000000..a8d8929
--- /dev/null
+++ b/manifests/dashboard/kubernetes-dashboard.yaml
@@ -0,0 +1,165 @@
+# Copyright 2017 The Kubernetes Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ------------------- Dashboard Secret ------------------- #
+
+apiVersion: v1
+kind: Secret
+metadata:
+ labels:
+ k8s-app: kubernetes-dashboard
+ name: kubernetes-dashboard-certs
+ namespace: kube-system
+type: Opaque
+
+---
+# ------------------- Dashboard Service Account ------------------- #
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ k8s-app: kubernetes-dashboard
+ name: kubernetes-dashboard
+ namespace: kube-system
+
+---
+# ------------------- Dashboard Role & Role Binding ------------------- #
+
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: kubernetes-dashboard-minimal
+ namespace: kube-system
+rules:
+ # Allow Dashboard to create 'kubernetes-dashboard-key-holder' secret.
+- apiGroups: [""]
+ resources: ["secrets"]
+ verbs: ["create"]
+ # Allow Dashboard to create 'kubernetes-dashboard-settings' config map.
+- apiGroups: [""]
+ resources: ["configmaps"]
+ verbs: ["create"]
+ # Allow Dashboard to get, update and delete Dashboard exclusive secrets.
+- apiGroups: [""]
+ resources: ["secrets"]
+ resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs"]
+ verbs: ["get", "update", "delete"]
+ # Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map.
+- apiGroups: [""]
+ resources: ["configmaps"]
+ resourceNames: ["kubernetes-dashboard-settings"]
+ verbs: ["get", "update"]
+ # Allow Dashboard to get metrics from heapster.
+- apiGroups: [""]
+ resources: ["services"]
+ resourceNames: ["heapster"]
+ verbs: ["proxy"]
+- apiGroups: [""]
+ resources: ["services/proxy"]
+ resourceNames: ["heapster", "http:heapster:", "https:heapster:"]
+ verbs: ["get"]
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: kubernetes-dashboard-minimal
+ namespace: kube-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: kubernetes-dashboard-minimal
+subjects:
+- kind: ServiceAccount
+ name: kubernetes-dashboard
+ namespace: kube-system
+
+---
+# ------------------- Dashboard Deployment ------------------- #
+
+kind: Deployment
+apiVersion: apps/v1beta2
+metadata:
+ labels:
+ k8s-app: kubernetes-dashboard
+ name: kubernetes-dashboard
+ namespace: kube-system
+spec:
+ replicas: 1
+ revisionHistoryLimit: 10
+ selector:
+ matchLabels:
+ k8s-app: kubernetes-dashboard
+ template:
+ metadata:
+ labels:
+ k8s-app: kubernetes-dashboard
+ spec:
+ containers:
+ - name: kubernetes-dashboard
+ image: mirrorgooglecontainers/kubernetes-dashboard-amd64:v1.10.1
+ ports:
+ - containerPort: 8443
+ protocol: TCP
+ args:
+ - --auto-generate-certificates
+ # Uncomment the following line to manually specify Kubernetes API server Host
+ # If not specified, Dashboard will attempt to auto discover the API server and connect
+ # to it. Uncomment only if the default does not work.
+ # - --apiserver-host=http://my-address:port
+ volumeMounts:
+ - name: kubernetes-dashboard-certs
+ mountPath: /certs
+ # Create on-disk volume to store exec logs
+ - mountPath: /tmp
+ name: tmp-volume
+ livenessProbe:
+ httpGet:
+ scheme: HTTPS
+ path: /
+ port: 8443
+ initialDelaySeconds: 30
+ timeoutSeconds: 30
+ volumes:
+ - name: kubernetes-dashboard-certs
+ secret:
+ secretName: kubernetes-dashboard-certs
+ - name: tmp-volume
+ emptyDir: {}
+ serviceAccountName: kubernetes-dashboard
+ # Comment the following tolerations if Dashboard must not be deployed on master
+ tolerations:
+ - key: node-role.kubernetes.io/master
+ effect: NoSchedule
+
+---
+# ------------------- Dashboard Service ------------------- #
+
+kind: Service
+apiVersion: v1
+metadata:
+ labels:
+ k8s-app: kubernetes-dashboard
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+ name: kubernetes-dashboard
+ namespace: kube-system
+spec:
+ ports:
+ - port: 443
+ targetPort: 8443
+ selector:
+ k8s-app: kubernetes-dashboard
+ type: NodePort
diff --git a/manifests/dashboard/read-user-sa-rbac.yaml b/manifests/dashboard/read-user-sa-rbac.yaml
new file mode 100644
index 0000000..3f832cd
--- /dev/null
+++ b/manifests/dashboard/read-user-sa-rbac.yaml
@@ -0,0 +1,149 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: dashboard-read-user
+ namespace: kube-system
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: dashboard-read-binding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: dashboard-read-clusterrole
+subjects:
+- kind: ServiceAccount
+ name: dashboard-read-user
+ namespace: kube-system
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: dashboard-read-clusterrole
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ - endpoints
+ - persistentvolumeclaims
+ - pods
+ - replicationcontrollers
+ - replicationcontrollers/scale
+ - serviceaccounts
+ - services
+ - nodes
+ - persistentvolumeclaims
+ - persistentvolumes
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - ""
+ resources:
+ - bindings
+ - events
+ - limitranges
+ - namespaces/status
+ - pods/log
+ - pods/status
+ - replicationcontrollers/status
+ - resourcequotas
+ - resourcequotas/status
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - ""
+ resources:
+ - namespaces
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - apps
+ resources:
+ - daemonsets
+ - deployments
+ - deployments/scale
+ - replicasets
+ - replicasets/scale
+ - statefulsets
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - autoscaling
+ resources:
+ - horizontalpodautoscalers
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - batch
+ resources:
+ - cronjobs
+ - jobs
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - extensions
+ resources:
+ - daemonsets
+ - deployments
+ - deployments/scale
+ - ingresses
+ - networkpolicies
+ - replicasets
+ - replicasets/scale
+ - replicationcontrollers/scale
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - policy
+ resources:
+ - poddisruptionbudgets
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - networking.k8s.io
+ resources:
+ - networkpolicies
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - storage.k8s.io
+ resources:
+ - storageclasses
+ - volumeattachments
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - rbac.authorization.k8s.io
+ resources:
+ - clusterrolebindings
+ - clusterroles
+ - roles
+ - rolebindings
+ verbs:
+ - get
+ - list
+ - watch
diff --git a/manifests/dashboard/ui-admin-rbac.yaml b/manifests/dashboard/ui-admin-rbac.yaml
new file mode 100644
index 0000000..ee39a7d
--- /dev/null
+++ b/manifests/dashboard/ui-admin-rbac.yaml
@@ -0,0 +1,27 @@
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: ui-admin
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - services
+ - services/proxy
+ verbs:
+ - '*'
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: ui-admin-binding
+ namespace: kube-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: ui-admin
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+ kind: User
+ name: admin
diff --git a/manifests/dashboard/ui-read-rbac.yaml b/manifests/dashboard/ui-read-rbac.yaml
new file mode 100644
index 0000000..b03c6b4
--- /dev/null
+++ b/manifests/dashboard/ui-read-rbac.yaml
@@ -0,0 +1,29 @@
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: ui-read
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - services
+ - services/proxy
+ verbs:
+ - get
+ - list
+ - watch
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: ui-read-binding
+ namespace: kube-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: ui-read
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+ kind: User
+ name: readonly
diff --git a/manifests/efk/es-dynamic-pv/es-statefulset.yaml b/manifests/efk/es-dynamic-pv/es-statefulset.yaml
new file mode 100644
index 0000000..0f5b111
--- /dev/null
+++ b/manifests/efk/es-dynamic-pv/es-statefulset.yaml
@@ -0,0 +1,117 @@
+# RBAC authn and authz
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: elasticsearch-logging
+ namespace: kube-system
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: elasticsearch-logging
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - "services"
+ - "namespaces"
+ - "endpoints"
+ verbs:
+ - "get"
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ namespace: kube-system
+ name: elasticsearch-logging
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+subjects:
+- kind: ServiceAccount
+ name: elasticsearch-logging
+ namespace: kube-system
+ apiGroup: ""
+roleRef:
+ kind: ClusterRole
+ name: elasticsearch-logging
+ apiGroup: ""
+---
+# Elasticsearch deployment itself
+apiVersion: apps/v1beta2
+kind: StatefulSet
+metadata:
+ name: elasticsearch-logging
+ namespace: kube-system
+ labels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+spec:
+ serviceName: elasticsearch-logging
+ replicas: 2
+ selector:
+ matchLabels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ template:
+ metadata:
+ labels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ kubernetes.io/cluster-service: "true"
+ spec:
+ serviceAccountName: elasticsearch-logging
+ containers:
+ #- image: gcr.io/google-containers/elasticsearch:v5.6.4
+ - image: mirrorgooglecontainers/elasticsearch:v5.6.4
+ name: elasticsearch-logging
+ resources:
+ # need more cpu upon initialization, therefore burstable class
+ limits:
+ cpu: 1000m
+ requests:
+ cpu: 100m
+ ports:
+ - containerPort: 9200
+ name: db
+ protocol: TCP
+ - containerPort: 9300
+ name: transport
+ protocol: TCP
+ volumeMounts:
+ - name: elasticsearch-logging
+ mountPath: /data
+ env:
+ - name: "NAMESPACE"
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ # Elasticsearch requires vm.max_map_count to be at least 262144.
+ # If your OS already sets up this number to a higher value, feel free
+ # to remove this init container.
+ initContainers:
+ - image: alpine:3.6
+ command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"]
+ name: elasticsearch-logging-init
+ securityContext:
+ privileged: true
+ volumeClaimTemplates:
+ - metadata:
+ name: elasticsearch-logging
+ spec:
+ accessModes: [ "ReadWriteMany" ]
+ storageClassName: "nfs-dynamic-class"
+ resources:
+ requests:
+ storage: 4Gi
diff --git a/manifests/efk/es-service.yaml b/manifests/efk/es-service.yaml
new file mode 100644
index 0000000..3c45e5c
--- /dev/null
+++ b/manifests/efk/es-service.yaml
@@ -0,0 +1,18 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: elasticsearch-logging
+ namespace: kube-system
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+ kubernetes.io/name: "Elasticsearch"
+spec:
+ ports:
+ - port: 9200
+ protocol: TCP
+ targetPort: db
+ clusterIP: None
+ selector:
+ k8s-app: elasticsearch-logging
diff --git a/manifests/efk/es-static-pv/es-pv0.yaml b/manifests/efk/es-static-pv/es-pv0.yaml
new file mode 100644
index 0000000..7686cc9
--- /dev/null
+++ b/manifests/efk/es-static-pv/es-pv0.yaml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: pv-es-0
+spec:
+ capacity:
+ storage: 4Gi
+ accessModes:
+ - ReadWriteMany
+ volumeMode: Filesystem
+ persistentVolumeReclaimPolicy: Recycle
+ storageClassName: "es-storage-class"
+ nfs:
+ # 根据实际共享目录修改
+ path: /share/es0
+ # 根据实际 nfs服务器地址修改
+ server: 192.168.1.208
diff --git a/manifests/efk/es-static-pv/es-pv1.yaml b/manifests/efk/es-static-pv/es-pv1.yaml
new file mode 100644
index 0000000..b41309e
--- /dev/null
+++ b/manifests/efk/es-static-pv/es-pv1.yaml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: pv-es-1
+spec:
+ capacity:
+ storage: 4Gi
+ accessModes:
+ - ReadWriteMany
+ volumeMode: Filesystem
+ persistentVolumeReclaimPolicy: Recycle
+ storageClassName: "es-storage-class"
+ nfs:
+ # 根据实际共享目录修改
+ path: /share/es1
+ # 根据实际 nfs服务器地址修改
+ server: 192.168.1.208
diff --git a/manifests/efk/es-static-pv/es-pv2.yaml b/manifests/efk/es-static-pv/es-pv2.yaml
new file mode 100644
index 0000000..f1f4c05
--- /dev/null
+++ b/manifests/efk/es-static-pv/es-pv2.yaml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: pv-es-2
+spec:
+ capacity:
+ storage: 4Gi
+ accessModes:
+ - ReadWriteMany
+ volumeMode: Filesystem
+ persistentVolumeReclaimPolicy: Recycle
+ storageClassName: "es-storage-class"
+ nfs:
+ # 根据实际共享目录修改
+ path: /share/es2
+ # 根据实际 nfs服务器地址修改
+ server: 192.168.1.208
diff --git a/manifests/efk/es-static-pv/es-statefulset.yaml b/manifests/efk/es-static-pv/es-statefulset.yaml
new file mode 100644
index 0000000..f8e7f4f
--- /dev/null
+++ b/manifests/efk/es-static-pv/es-statefulset.yaml
@@ -0,0 +1,117 @@
+# RBAC authn and authz
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: elasticsearch-logging
+ namespace: kube-system
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: elasticsearch-logging
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - "services"
+ - "namespaces"
+ - "endpoints"
+ verbs:
+ - "get"
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ namespace: kube-system
+ name: elasticsearch-logging
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+subjects:
+- kind: ServiceAccount
+ name: elasticsearch-logging
+ namespace: kube-system
+ apiGroup: ""
+roleRef:
+ kind: ClusterRole
+ name: elasticsearch-logging
+ apiGroup: ""
+---
+# Elasticsearch deployment itself
+apiVersion: apps/v1beta2
+kind: StatefulSet
+metadata:
+ name: elasticsearch-logging
+ namespace: kube-system
+ labels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+spec:
+ serviceName: elasticsearch-logging
+ replicas: 2
+ selector:
+ matchLabels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ template:
+ metadata:
+ labels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ kubernetes.io/cluster-service: "true"
+ spec:
+ serviceAccountName: elasticsearch-logging
+ containers:
+ #- image: gcr.io/google-containers/elasticsearch:v5.6.4
+ - image: mirrorgooglecontainers/elasticsearch:v5.6.4
+ name: elasticsearch-logging
+ resources:
+ # need more cpu upon initialization, therefore burstable class
+ limits:
+ cpu: 1000m
+ requests:
+ cpu: 100m
+ ports:
+ - containerPort: 9200
+ name: db
+ protocol: TCP
+ - containerPort: 9300
+ name: transport
+ protocol: TCP
+ volumeMounts:
+ - name: elasticsearch-logging
+ mountPath: /data
+ env:
+ - name: "NAMESPACE"
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ # Elasticsearch requires vm.max_map_count to be at least 262144.
+ # If your OS already sets up this number to a higher value, feel free
+ # to remove this init container.
+ initContainers:
+ - image: alpine:3.6
+ command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"]
+ name: elasticsearch-logging-init
+ securityContext:
+ privileged: true
+ volumeClaimTemplates:
+ - metadata:
+ name: elasticsearch-logging
+ spec:
+ accessModes: [ "ReadWriteMany" ]
+ storageClassName: "es-storage-class"
+ resources:
+ requests:
+ storage: 4Gi
diff --git a/manifests/efk/es-without-pv/es-statefulset.yaml b/manifests/efk/es-without-pv/es-statefulset.yaml
new file mode 100644
index 0000000..4bd6584
--- /dev/null
+++ b/manifests/efk/es-without-pv/es-statefulset.yaml
@@ -0,0 +1,111 @@
+# RBAC authn and authz
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: elasticsearch-logging
+ namespace: kube-system
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: elasticsearch-logging
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - "services"
+ - "namespaces"
+ - "endpoints"
+ verbs:
+ - "get"
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ namespace: kube-system
+ name: elasticsearch-logging
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+subjects:
+- kind: ServiceAccount
+ name: elasticsearch-logging
+ namespace: kube-system
+ apiGroup: ""
+roleRef:
+ kind: ClusterRole
+ name: elasticsearch-logging
+ apiGroup: ""
+---
+# Elasticsearch deployment itself
+apiVersion: apps/v1beta2
+kind: StatefulSet
+metadata:
+ name: elasticsearch-logging
+ namespace: kube-system
+ labels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+spec:
+ serviceName: elasticsearch-logging
+ replicas: 2
+ selector:
+ matchLabels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ template:
+ metadata:
+ labels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ kubernetes.io/cluster-service: "true"
+ spec:
+ serviceAccountName: elasticsearch-logging
+ containers:
+ #- image: gcr.io/google-containers/elasticsearch:v5.6.4
+ - image: mirrorgooglecontainers/elasticsearch:v5.6.4
+ name: elasticsearch-logging
+ resources:
+ # need more cpu upon initialization, therefore burstable class
+ limits:
+ cpu: 1000m
+ requests:
+ cpu: 100m
+ ports:
+ - containerPort: 9200
+ name: db
+ protocol: TCP
+ - containerPort: 9300
+ name: transport
+ protocol: TCP
+ volumeMounts:
+ - name: elasticsearch-logging
+ mountPath: /data
+ env:
+ - name: "NAMESPACE"
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ volumes:
+ - name: elasticsearch-logging
+ emptyDir: {}
+ # Elasticsearch requires vm.max_map_count to be at least 262144.
+ # If your OS already sets up this number to a higher value, feel free
+ # to remove this init container.
+ initContainers:
+ - image: alpine:3.6
+ command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"]
+ name: elasticsearch-logging-init
+ securityContext:
+ privileged: true
diff --git a/manifests/efk/fluentd-es-configmap.yaml b/manifests/efk/fluentd-es-configmap.yaml
new file mode 100644
index 0000000..09fbad0
--- /dev/null
+++ b/manifests/efk/fluentd-es-configmap.yaml
@@ -0,0 +1,373 @@
+kind: ConfigMap
+apiVersion: v1
+data:
+ containers.input.conf: |-
+ # This configuration file for Fluentd / td-agent is used
+ # to watch changes to Docker log files. The kubelet creates symlinks that
+ # capture the pod name, namespace, container name & Docker container ID
+ # to the docker logs for pods in the /var/log/containers directory on the host.
+ # If running this fluentd configuration in a Docker container, the /var/log
+ # directory should be mounted in the container.
+ #
+ # These logs are then submitted to Elasticsearch which assumes the
+ # installation of the fluent-plugin-elasticsearch & the
+ # fluent-plugin-kubernetes_metadata_filter plugins.
+ # See https://github.com/uken/fluent-plugin-elasticsearch &
+ # https://github.com/fabric8io/fluent-plugin-kubernetes_metadata_filter for
+ # more information about the plugins.
+ #
+ # Example
+ # =======
+ # A line in the Docker log file might look like this JSON:
+ #
+ # {"log":"2014/09/25 21:15:03 Got request with path wombat\n",
+ # "stream":"stderr",
+ # "time":"2014-09-25T21:15:03.499185026Z"}
+ #
+ # The time_format specification below makes sure we properly
+ # parse the time format produced by Docker. This will be
+ # submitted to Elasticsearch and should appear like:
+ # $ curl 'http://elasticsearch-logging:9200/_search?pretty'
+ # ...
+ # {
+ # "_index" : "logstash-2014.09.25",
+ # "_type" : "fluentd",
+ # "_id" : "VBrbor2QTuGpsQyTCdfzqA",
+ # "_score" : 1.0,
+ # "_source":{"log":"2014/09/25 22:45:50 Got request with path wombat\n",
+ # "stream":"stderr","tag":"docker.container.all",
+ # "@timestamp":"2014-09-25T22:45:50+00:00"}
+ # },
+ # ...
+ #
+ # The Kubernetes fluentd plugin is used to write the Kubernetes metadata to the log
+ # record & add labels to the log record if properly configured. This enables users
+ # to filter & search logs on any metadata.
+ # For example a Docker container's logs might be in the directory:
+ #
+ # /var/lib/docker/containers/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b
+ #
+ # and in the file:
+ #
+ # 997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b-json.log
+ #
+ # where 997599971ee6... is the Docker ID of the running container.
+ # The Kubernetes kubelet makes a symbolic link to this file on the host machine
+ # in the /var/log/containers directory which includes the pod name and the Kubernetes
+ # container name:
+ #
+ # synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
+ # ->
+ # /var/lib/docker/containers/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b-json.log
+ #
+ # The /var/log directory on the host is mapped to the /var/log directory in the container
+ # running this instance of Fluentd and we end up collecting the file:
+ #
+ # /var/log/containers/synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
+ #
+ # This results in the tag:
+ #
+ # var.log.containers.synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
+ #
+ # The Kubernetes fluentd plugin is used to extract the namespace, pod name & container name
+ # which are added to the log message as a kubernetes field object & the Docker container ID
+ # is also added under the docker field object.
+ # The final tag is:
+ #
+ # kubernetes.var.log.containers.synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
+ #
+ # And the final log record look like:
+ #
+ # {
+ # "log":"2014/09/25 21:15:03 Got request with path wombat\n",
+ # "stream":"stderr",
+ # "time":"2014-09-25T21:15:03.499185026Z",
+ # "kubernetes": {
+ # "namespace": "default",
+ # "pod_name": "synthetic-logger-0.25lps-pod",
+ # "container_name": "synth-lgr"
+ # },
+ # "docker": {
+ # "container_id": "997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b"
+ # }
+ # }
+ #
+ # This makes it easier for users to search for logs by pod name or by
+ # the name of the Kubernetes container regardless of how many times the
+ # Kubernetes pod has been restarted (resulting in a several Docker container IDs).
+
+ # Json Log Example:
+ # {"log":"[info:2016-02-16T16:04:05.930-08:00] Some log text here\n","stream":"stdout","time":"2016-02-17T00:04:05.931087621Z"}
+ # CRI Log Example:
+ # 2016-02-17T00:04:05.931087621Z stdout F [info:2016-02-16T16:04:05.930-08:00] Some log text here
+
+ type tail
+ path /var/log/containers/*.log
+ pos_file /var/log/es-containers.log.pos
+ time_format %Y-%m-%dT%H:%M:%S.%NZ
+ tag kubernetes.*
+ read_from_head true
+ format multi_format
+
+ format json
+ time_key time
+ time_format %Y-%m-%dT%H:%M:%S.%NZ
+
+
+ format /^(?.+) (?stdout|stderr) [^ ]* (?.*)$/
+ time_format %Y-%m-%dT%H:%M:%S.%N%:z
+
+
+ system.input.conf: |-
+ # Example:
+ # 2015-12-21 23:17:22,066 [salt.state ][INFO ] Completed state [net.ipv4.ip_forward] at time 23:17:22.066081
+
+ type tail
+ format /^(?[^ ]* [^ ,]*)[^\[]*\[[^\]]*\]\[(?[^ \]]*) *\] (?.*)$/
+ time_format %Y-%m-%d %H:%M:%S
+ path /var/log/salt/minion
+ pos_file /var/log/es-salt.pos
+ tag salt
+
+
+ # Example:
+ # Dec 21 23:17:22 gke-foo-1-1-4b5cbd14-node-4eoj startupscript: Finished running startup script /var/run/google.startup.script
+
+ type tail
+ format syslog
+ path /var/log/startupscript.log
+ pos_file /var/log/es-startupscript.log.pos
+ tag startupscript
+
+
+ # Examples:
+ # time="2016-02-04T06:51:03.053580605Z" level=info msg="GET /containers/json"
+ # time="2016-02-04T07:53:57.505612354Z" level=error msg="HTTP Error" err="No such image: -f" statusCode=404
+
+ type tail
+ format /^time="(?[^)]*)" level=(?[^ ]*) msg="(?[^"]*)"( err="(?[^"]*)")?( statusCode=($\d+))?/
+ path /var/log/docker.log
+ pos_file /var/log/es-docker.log.pos
+ tag docker
+
+
+ # Example:
+ # 2016/02/04 06:52:38 filePurge: successfully removed file /var/etcd/data/member/wal/00000000000006d0-00000000010a23d1.wal
+
+ type tail
+ # Not parsing this, because it doesn't have anything particularly useful to
+ # parse out of it (like severities).
+ format none
+ path /var/log/etcd.log
+ pos_file /var/log/es-etcd.log.pos
+ tag etcd
+
+
+ # Multi-line parsing is required for all the kube logs because very large log
+ # statements, such as those that include entire object bodies, get split into
+ # multiple lines by glog.
+
+ # Example:
+ # I0204 07:32:30.020537 3368 server.go:1048] POST /stats/container/: (13.972191ms) 200 [[Go-http-client/1.1] 10.244.1.3:40537]
+
+ type tail
+ format multiline
+ multiline_flush_interval 5s
+ format_firstline /^\w\d{4}/
+ format1 /^(?\w)(?\d{4} [^\s]*)\s+(?\d+)\s+(?[^ \]]+)\] (?.*)/
+ time_format %m%d %H:%M:%S.%N
+ path /var/log/kubelet.log
+ pos_file /var/log/es-kubelet.log.pos
+ tag kubelet
+
+
+ # Example:
+ # I1118 21:26:53.975789 6 proxier.go:1096] Port "nodePort for kube-system/default-http-backend:http" (:31429/tcp) was open before and is still needed
+
+ type tail
+ format multiline
+ multiline_flush_interval 5s
+ format_firstline /^\w\d{4}/
+ format1 /^(?\w)(?\d{4} [^\s]*)\s+(?\d+)\s+(?[^ \]]+)\] (?.*)/
+ time_format %m%d %H:%M:%S.%N
+ path /var/log/kube-proxy.log
+ pos_file /var/log/es-kube-proxy.log.pos
+ tag kube-proxy
+
+
+ # Example:
+ # I0204 07:00:19.604280 5 handlers.go:131] GET /api/v1/nodes: (1.624207ms) 200 [[kube-controller-manager/v1.1.3 (linux/amd64) kubernetes/6a81b50] 127.0.0.1:38266]
+
+ type tail
+ format multiline
+ multiline_flush_interval 5s
+ format_firstline /^\w\d{4}/
+ format1 /^(?\w)(?\d{4} [^\s]*)\s+(?\d+)\s+(?[^ \]]+)\] (?.*)/
+ time_format %m%d %H:%M:%S.%N
+ path /var/log/kube-apiserver.log
+ pos_file /var/log/es-kube-apiserver.log.pos
+ tag kube-apiserver
+
+
+ # Example:
+ # I0204 06:55:31.872680 5 servicecontroller.go:277] LB already exists and doesn't need update for service kube-system/kube-ui
+
+ type tail
+ format multiline
+ multiline_flush_interval 5s
+ format_firstline /^\w\d{4}/
+ format1 /^(?\w)(?\d{4} [^\s]*)\s+(?\d+)\s+(?[^ \]]+)\] (?.*)/
+ time_format %m%d %H:%M:%S.%N
+ path /var/log/kube-controller-manager.log
+ pos_file /var/log/es-kube-controller-manager.log.pos
+ tag kube-controller-manager
+
+
+ # Example:
+ # W0204 06:49:18.239674 7 reflector.go:245] pkg/scheduler/factory/factory.go:193: watch of *api.Service ended with: 401: The event in requested index is outdated and cleared (the requested history has been cleared [2578313/2577886]) [2579312]
+
+ type tail
+ format multiline
+ multiline_flush_interval 5s
+ format_firstline /^\w\d{4}/
+ format1 /^(?\w)(?\d{4} [^\s]*)\s+(?\d+)\s+(?[^ \]]+)\] (?.*)/
+ time_format %m%d %H:%M:%S.%N
+ path /var/log/kube-scheduler.log
+ pos_file /var/log/es-kube-scheduler.log.pos
+ tag kube-scheduler
+
+
+ # Example:
+ # I1104 10:36:20.242766 5 rescheduler.go:73] Running Rescheduler
+
+ type tail
+ format multiline
+ multiline_flush_interval 5s
+ format_firstline /^\w\d{4}/
+ format1 /^(?\w)(?\d{4} [^\s]*)\s+(?\d+)\s+(?[^ \]]+)\] (?.*)/
+ time_format %m%d %H:%M:%S.%N
+ path /var/log/rescheduler.log
+ pos_file /var/log/es-rescheduler.log.pos
+ tag rescheduler
+
+
+ # Example:
+ # I0603 15:31:05.793605 6 cluster_manager.go:230] Reading config from path /etc/gce.conf
+
+ type tail
+ format multiline
+ multiline_flush_interval 5s
+ format_firstline /^\w\d{4}/
+ format1 /^(?\w)(?\d{4} [^\s]*)\s+(?\d+)\s+(?[^ \]]+)\] (?.*)/
+ time_format %m%d %H:%M:%S.%N
+ path /var/log/glbc.log
+ pos_file /var/log/es-glbc.log.pos
+ tag glbc
+
+
+ # Example:
+ # I0603 15:31:05.793605 6 cluster_manager.go:230] Reading config from path /etc/gce.conf
+
+ type tail
+ format multiline
+ multiline_flush_interval 5s
+ format_firstline /^\w\d{4}/
+ format1 /^(?\w)(?\d{4} [^\s]*)\s+(?\d+)\s+(?[^ \]]+)\] (?.*)/
+ time_format %m%d %H:%M:%S.%N
+ path /var/log/cluster-autoscaler.log
+ pos_file /var/log/es-cluster-autoscaler.log.pos
+ tag cluster-autoscaler
+
+
+ # Logs from systemd-journal for interesting services.
+
+ type systemd
+ filters [{ "_SYSTEMD_UNIT": "docker.service" }]
+ pos_file /var/log/gcp-journald-docker.pos
+ read_from_head true
+ tag docker
+
+
+
+ type systemd
+ filters [{ "_SYSTEMD_UNIT": "kubelet.service" }]
+ pos_file /var/log/gcp-journald-kubelet.pos
+ read_from_head true
+ tag kubelet
+
+
+
+ type systemd
+ filters [{ "_SYSTEMD_UNIT": "node-problem-detector.service" }]
+ pos_file /var/log/gcp-journald-node-problem-detector.pos
+ read_from_head true
+ tag node-problem-detector
+
+ forward.input.conf: |-
+ # Takes the messages sent over TCP
+
+ type forward
+
+ monitoring.conf: |-
+ # Prometheus Exporter Plugin
+ # input plugin that exports metrics
+
+ @type prometheus
+
+
+
+ @type monitor_agent
+
+
+ # input plugin that collects metrics from MonitorAgent
+
+ @type prometheus_monitor
+
+ host ${hostname}
+
+
+
+ # input plugin that collects metrics for output plugin
+
+ @type prometheus_output_monitor
+
+ host ${hostname}
+
+
+
+ # input plugin that collects metrics for in_tail plugin
+
+ @type prometheus_tail_monitor
+
+ host ${hostname}
+
+
+ output.conf: |-
+ # Enriches records with Kubernetes metadata
+
+ type kubernetes_metadata
+
+
+
+ type elasticsearch
+ log_level info
+ include_tag_key true
+ host elasticsearch-logging
+ port 9200
+ logstash_format true
+ # Set the chunk limits.
+ buffer_chunk_limit 2M
+ buffer_queue_limit 8
+ flush_interval 5s
+ # Never wait longer than 5 minutes between retries.
+ max_retry_wait 30
+ # Disable the limit on the number of retries (retry forever).
+ disable_retry_limit
+ # Use multiple threads for processing.
+ num_threads 2
+
+metadata:
+ name: fluentd-es-config-v0.1.1
+ namespace: kube-system
+ labels:
+ addonmanager.kubernetes.io/mode: Reconcile
diff --git a/manifests/efk/fluentd-es-ds.yaml b/manifests/efk/fluentd-es-ds.yaml
new file mode 100644
index 0000000..0417538
--- /dev/null
+++ b/manifests/efk/fluentd-es-ds.yaml
@@ -0,0 +1,116 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: fluentd-es
+ namespace: kube-system
+ labels:
+ k8s-app: fluentd-es
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: fluentd-es
+ labels:
+ k8s-app: fluentd-es
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - "namespaces"
+ - "pods"
+ verbs:
+ - "get"
+ - "watch"
+ - "list"
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: fluentd-es
+ labels:
+ k8s-app: fluentd-es
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+subjects:
+- kind: ServiceAccount
+ name: fluentd-es
+ namespace: kube-system
+ apiGroup: ""
+roleRef:
+ kind: ClusterRole
+ name: fluentd-es
+ apiGroup: ""
+---
+apiVersion: apps/v1beta2
+kind: DaemonSet
+metadata:
+ name: fluentd-es-v2.0.2
+ namespace: kube-system
+ labels:
+ k8s-app: fluentd-es
+ version: v2.0.2
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+spec:
+ selector:
+ matchLabels:
+ k8s-app: fluentd-es
+ version: v2.0.2
+ template:
+ metadata:
+ labels:
+ k8s-app: fluentd-es
+ kubernetes.io/cluster-service: "true"
+ version: v2.0.2
+ # This annotation ensures that fluentd does not get evicted if the node
+ # supports critical pod annotation based priority scheme.
+ # Note that this does not guarantee admission on the nodes (#40573).
+ annotations:
+ scheduler.alpha.kubernetes.io/critical-pod: ''
+ spec:
+ serviceAccountName: fluentd-es
+ containers:
+ - name: fluentd-es
+ #image: gcr.io/google-containers/fluentd-elasticsearch:v2.0.2
+ image: mirrorgooglecontainers/fluentd-elasticsearch:v2.0.2
+ env:
+ - name: FLUENTD_ARGS
+ value: --no-supervisor -q
+ resources:
+ limits:
+ memory: 500Mi
+ requests:
+ cpu: 100m
+ memory: 200Mi
+ volumeMounts:
+ - name: varlog
+ mountPath: /var/log
+ - name: varlibdockercontainers
+ mountPath: /var/lib/docker/containers
+ readOnly: true
+ - name: libsystemddir
+ mountPath: /host/lib
+ readOnly: true
+ - name: config-volume
+ mountPath: /etc/fluent/config.d
+ nodeSelector:
+ beta.kubernetes.io/fluentd-ds-ready: "true"
+ terminationGracePeriodSeconds: 30
+ volumes:
+ - name: varlog
+ hostPath:
+ path: /var/log
+ - name: varlibdockercontainers
+ hostPath:
+ path: /var/lib/docker/containers
+ # It is needed to copy systemd library to decompress journals
+ - name: libsystemddir
+ hostPath:
+ path: /usr/lib64
+ - name: config-volume
+ configMap:
+ name: fluentd-es-config-v0.1.1
diff --git a/manifests/efk/kibana-deployment.yaml b/manifests/efk/kibana-deployment.yaml
new file mode 100644
index 0000000..676abc7
--- /dev/null
+++ b/manifests/efk/kibana-deployment.yaml
@@ -0,0 +1,43 @@
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: kibana-logging
+ namespace: kube-system
+ labels:
+ k8s-app: kibana-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: kibana-logging
+ template:
+ metadata:
+ labels:
+ k8s-app: kibana-logging
+ spec:
+ containers:
+ - name: kibana-logging
+ #image: docker.elastic.co/kibana/kibana:5.6.4
+ image: jmgao1983/kibana:5.6.4
+ resources:
+ # need more cpu upon initialization, therefore burstable class
+ limits:
+ cpu: 1000m
+ requests:
+ cpu: 100m
+ env:
+ - name: ELASTICSEARCH_URL
+ value: http://elasticsearch-logging:9200
+ - name: SERVER_BASEPATH
+ #value: /api/v1/proxy/namespaces/kube-system/services/kibana-logging
+ value: /api/v1/namespaces/kube-system/services/kibana-logging/proxy
+ - name: XPACK_MONITORING_ENABLED
+ value: "false"
+ - name: XPACK_SECURITY_ENABLED
+ value: "false"
+ ports:
+ - containerPort: 5601
+ name: ui
+ protocol: TCP
diff --git a/manifests/efk/kibana-service.yaml b/manifests/efk/kibana-service.yaml
new file mode 100644
index 0000000..d241ef7
--- /dev/null
+++ b/manifests/efk/kibana-service.yaml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: kibana-logging
+ namespace: kube-system
+ labels:
+ k8s-app: kibana-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+ kubernetes.io/name: "Kibana"
+spec:
+ ports:
+ - port: 5601
+ protocol: TCP
+ targetPort: ui
+ selector:
+ k8s-app: kibana-logging
diff --git a/manifests/es-cluster/elasticsearch/.helmignore b/manifests/es-cluster/elasticsearch/.helmignore
new file mode 100644
index 0000000..f225651
--- /dev/null
+++ b/manifests/es-cluster/elasticsearch/.helmignore
@@ -0,0 +1,3 @@
+.git
+# OWNERS file for Kubernetes
+OWNERS
\ No newline at end of file
diff --git a/manifests/es-cluster/elasticsearch/Chart.yaml b/manifests/es-cluster/elasticsearch/Chart.yaml
new file mode 100755
index 0000000..84c8dde
--- /dev/null
+++ b/manifests/es-cluster/elasticsearch/Chart.yaml
@@ -0,0 +1,21 @@
+name: elasticsearch
+home: https://www.elastic.co/products/elasticsearch
+version: 1.7.2
+appVersion: 6.4.0
+description: Flexible and powerful open source, distributed real-time search and analytics
+ engine.
+icon: https://static-www.elastic.co/assets/blteb1c97719574938d/logo-elastic-elasticsearch-lt.svg
+sources:
+- https://www.elastic.co/products/elasticsearch
+- https://github.com/jetstack/elasticsearch-pet
+- https://github.com/giantswarm/kubernetes-elastic-stack
+- https://github.com/GoogleCloudPlatform/elasticsearch-docker
+- https://github.com/clockworksoul/helm-elasticsearch
+- https://github.com/pires/kubernetes-elasticsearch-cluster
+maintainers:
+- name: simonswine
+ email: christian@jetstack.io
+- name: icereval
+ email: michael.haselton@gmail.com
+- name: rendhalver
+ email: pete.brown@powerhrg.com
diff --git a/manifests/es-cluster/elasticsearch/OWNERS b/manifests/es-cluster/elasticsearch/OWNERS
new file mode 100644
index 0000000..89705d6
--- /dev/null
+++ b/manifests/es-cluster/elasticsearch/OWNERS
@@ -0,0 +1,8 @@
+approvers:
+- simonswine
+- icereval
+- rendhalver
+reviewers:
+- simonswine
+- icereval
+- rendhalver
diff --git a/manifests/es-cluster/elasticsearch/README.md b/manifests/es-cluster/elasticsearch/README.md
new file mode 100644
index 0000000..e29464f
--- /dev/null
+++ b/manifests/es-cluster/elasticsearch/README.md
@@ -0,0 +1,190 @@
+# Elasticsearch Helm Chart
+
+This chart uses a standard Docker image of Elasticsearch (docker.elastic.co/elasticsearch/elasticsearch-oss) and uses a service pointing to the master's transport port for service discovery.
+Elasticsearch does not communicate with the Kubernetes API, hence no need for RBAC permissions.
+
+## Warning for previous users
+If you are currently using an earlier version of this Chart you will need to redeploy your Elasticsearch clusters. The discovery method used here is incompatible with using RBAC.
+If you are upgrading to Elasticsearch 6 from the 5.5 version used in this chart before, please note that your cluster needs to do a full cluster restart.
+The simplest way to do that is to delete the installation (keep the PVs) and install this chart again with the new version.
+If you want to avoid doing that upgrade to Elasticsearch 5.6 first before moving on to Elasticsearch 6.0.
+
+## Prerequisites Details
+
+* Kubernetes 1.6+
+* PV dynamic provisioning support on the underlying infrastructure
+
+## StatefulSets Details
+* https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/
+
+## StatefulSets Caveats
+* https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#limitations
+
+## Todo
+
+* Implement TLS/Auth/Security
+* Smarter upscaling/downscaling
+* Solution for memory locking
+
+## Chart Details
+This chart will do the following:
+
+* Implemented a dynamically scalable elasticsearch cluster using Kubernetes StatefulSets/Deployments
+* Multi-role deployment: master, client (coordinating) and data nodes
+* Statefulset Supports scaling down without degrading the cluster
+
+## Installing the Chart
+
+To install the chart with the release name `my-release`:
+
+```bash
+$ helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
+$ helm install --name my-release incubator/elasticsearch
+```
+
+## Deleting the Charts
+
+Delete the Helm deployment as normal
+
+```
+$ helm delete my-release
+```
+
+Deletion of the StatefulSet doesn't cascade to deleting associated PVCs. To delete them:
+
+```
+$ kubectl delete pvc -l release=my-release,component=data
+```
+
+## Configuration
+
+The following table lists the configurable parameters of the elasticsearch chart and their default values.
+
+| Parameter | Description | Default |
+| ------------------------------------ | ------------------------------------------------------------------- | ------------------------------------ |
+| `appVersion` | Application Version (Elasticsearch) | `6.4.0` |
+| `image.repository` | Container image name | `docker.elastic.co/elasticsearch/elasticsearch-oss` |
+| `image.tag` | Container image tag | `6.4.0` |
+| `image.pullPolicy` | Container pull policy | `Always` |
+| `cluster.name` | Cluster name | `elasticsearch` |
+| `cluster.xpackEnable` | Writes the X-Pack configuration options to the configuration file | `false` |
+| `cluster.config` | Additional cluster config appended | `{}` |
+| `cluster.keystoreSecret` | Name of secret holding secure config options in an es keystore | `nil` |
+| `cluster.env` | Cluster environment variables | `{MINIMUM_MASTER_NODES: "2"}` |
+| `client.name` | Client component name | `client` |
+| `client.replicas` | Client node replicas (deployment) | `2` |
+| `client.resources` | Client node resources requests & limits | `{} - cpu limit must be an integer` |
+| `client.priorityClassName` | Client priorityClass | `nil` |
+| `client.heapSize` | Client node heap size | `512m` |
+| `client.podAnnotations` | Client Deployment annotations | `{}` |
+| `client.nodeSelector` | Node labels for client pod assignment | `{}` |
+| `client.tolerations` | Client tolerations | `[]` |
+| `client.serviceAnnotations` | Client Service annotations | `{}` |
+| `client.serviceType` | Client service type | `ClusterIP` |
+| `client.loadBalancerIP` | Client loadBalancerIP | `{}` |
+| `client.loadBalancerSourceRanges` | Client loadBalancerSourceRanges | `{}` |
+| `master.exposeHttp` | Expose http port 9200 on master Pods for monitoring, etc | `false` |
+| `master.name` | Master component name | `master` |
+| `master.replicas` | Master node replicas (deployment) | `2` |
+| `master.resources` | Master node resources requests & limits | `{} - cpu limit must be an integer` |
+| `master.priorityClassName` | Master priorityClass | `nil` |
+| `master.podAnnotations` | Master Deployment annotations | `{}` |
+| `master.nodeSelector` | Node labels for master pod assignment | `{}` |
+| `master.tolerations` | Master tolerations | `[]` |
+| `master.heapSize` | Master node heap size | `512m` |
+| `master.name` | Master component name | `master` |
+| `master.persistence.enabled` | Master persistent enabled/disabled | `true` |
+| `master.persistence.name` | Master statefulset PVC template name | `data` |
+| `master.persistence.size` | Master persistent volume size | `4Gi` |
+| `master.persistence.storageClass` | Master persistent volume Class | `nil` |
+| `master.persistence.accessMode` | Master persistent Access Mode | `ReadWriteOnce` |
+| `data.exposeHttp` | Expose http port 9200 on data Pods for monitoring, etc | `false` |
+| `data.replicas` | Data node replicas (statefulset) | `2` |
+| `data.resources` | Data node resources requests & limits | `{} - cpu limit must be an integer` |
+| `data.priorityClassName` | Data priorityClass | `nil` |
+| `data.heapSize` | Data node heap size | `1536m` |
+| `data.persistence.enabled` | Data persistent enabled/disabled | `true` |
+| `data.persistence.name` | Data statefulset PVC template name | `data` |
+| `data.persistence.size` | Data persistent volume size | `30Gi` |
+| `data.persistence.storageClass` | Data persistent volume Class | `nil` |
+| `data.persistence.accessMode` | Data persistent Access Mode | `ReadWriteOnce` |
+| `data.podAnnotations` | Data StatefulSet annotations | `{}` |
+| `data.nodeSelector` | Node labels for data pod assignment | `{}` |
+| `data.tolerations` | Data tolerations | `[]` |
+| `data.terminationGracePeriodSeconds` | Data termination grace period (seconds) | `3600` |
+| `data.antiAffinity` | Data anti-affinity policy | `soft` |
+
+Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`.
+
+In terms of Memory resources you should make sure that you follow that equation:
+
+- `${role}HeapSize < ${role}MemoryRequests < ${role}MemoryLimits`
+
+The YAML value of cluster.config is appended to elasticsearch.yml file for additional customization ("script.inline: on" for example to allow inline scripting)
+
+# Deep dive
+
+## Application Version
+
+This chart aims to support Elasticsearch v2 and v5 deployments by specifying the `values.yaml` parameter `appVersion`.
+
+### Version Specific Features
+
+* Memory Locking *(variable renamed)*
+* Ingest Node *(v5)*
+* X-Pack Plugin *(v5)*
+
+Upgrade paths & more info: https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-upgrade.html
+
+## Mlocking
+
+This is a limitation in kubernetes right now. There is no way to raise the
+limits of lockable memory, so that these memory areas won't be swapped. This
+would degrade performance heavily. The issue is tracked in
+[kubernetes/#3595](https://github.com/kubernetes/kubernetes/issues/3595).
+
+```
+[WARN ][bootstrap] Unable to lock JVM Memory: error=12,reason=Cannot allocate memory
+[WARN ][bootstrap] This can result in part of the JVM being swapped out.
+[WARN ][bootstrap] Increase RLIMIT_MEMLOCK, soft limit: 65536, hard limit: 65536
+```
+
+## Minimum Master Nodes
+> The minimum_master_nodes setting is extremely important to the stability of your cluster. This setting helps prevent split brains, the existence of two masters in a single cluster.
+
+>When you have a split brain, your cluster is at danger of losing data. Because the master is considered the supreme ruler of the cluster, it decides when new indices can be created, how shards are moved, and so forth. If you have two masters, data integrity becomes perilous, since you have two nodes that think they are in charge.
+
+>This setting tells Elasticsearch to not elect a master unless there are enough master-eligible nodes available. Only then will an election take place.
+
+>This setting should always be configured to a quorum (majority) of your master-eligible nodes. A quorum is (number of master-eligible nodes / 2) + 1
+
+More info: https://www.elastic.co/guide/en/elasticsearch/guide/1.x/_important_configuration_changes.html#_minimum_master_nodes
+
+# Client and Coordinating Nodes
+
+Elasticsearch v5 terminology has updated, and now refers to a `Client Node` as a `Coordinating Node`.
+
+More info: https://www.elastic.co/guide/en/elasticsearch/reference/5.5/modules-node.html#coordinating-node
+
+## Select right storage class for SSD volumes
+
+### GCE + Kubernetes 1.5
+
+Create StorageClass for SSD-PD
+
+```
+$ kubectl create -f - < >(tee -a "/var/log/elasticsearch-hooks.log")
+ NODE_NAME=${HOSTNAME}
+ echo "Prepare to migrate data of the node ${NODE_NAME}"
+ echo "Move all data from node ${NODE_NAME}"
+ curl -s -XPUT -H 'Content-Type: application/json' '{{ template "elasticsearch.client.fullname" . }}:9200/_cluster/settings' -d "{
+ \"transient\" :{
+ \"cluster.routing.allocation.exclude._name\" : \"${NODE_NAME}\"
+ }
+ }"
+ echo ""
+
+ while true ; do
+ echo -e "Wait for node ${NODE_NAME} to become empty"
+ SHARDS_ALLOCATION=$(curl -s -XGET 'http://{{ template "elasticsearch.client.fullname" . }}:9200/_cat/shards')
+ if ! echo "${SHARDS_ALLOCATION}" | grep -E "${NODE_NAME}"; then
+ break
+ fi
+ sleep 1
+ done
+ echo "Node ${NODE_NAME} is ready to shutdown"
+ post-start-hook.sh: |-
+ #!/bin/bash
+ exec &> >(tee -a "/var/log/elasticsearch-hooks.log")
+ NODE_NAME=${HOSTNAME}
+ CLUSTER_SETTINGS=$(curl -s -XGET "http://{{ template "elasticsearch.client.fullname" . }}:9200/_cluster/settings")
+ if echo "${CLUSTER_SETTINGS}" | grep -E "${NODE_NAME}"; then
+ echo "Activate node ${NODE_NAME}"
+ curl -s -XPUT -H 'Content-Type: application/json' "http://{{ template "elasticsearch.client.fullname" . }}:9200/_cluster/settings" -d "{
+ \"transient\" :{
+ \"cluster.routing.allocation.exclude._name\" : null
+ }
+ }"
+ fi
+ echo "Node ${NODE_NAME} is ready to be used"
diff --git a/manifests/es-cluster/elasticsearch/templates/data-pdb.yaml b/manifests/es-cluster/elasticsearch/templates/data-pdb.yaml
new file mode 100644
index 0000000..54e91c7
--- /dev/null
+++ b/manifests/es-cluster/elasticsearch/templates/data-pdb.yaml
@@ -0,0 +1,24 @@
+{{- if .Values.data.podDisruptionBudget.enabled }}
+apiVersion: policy/v1beta1
+kind: PodDisruptionBudget
+metadata:
+ labels:
+ app: {{ template "elasticsearch.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.data.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "elasticsearch.data.fullname" . }}
+spec:
+{{- if .Values.data.podDisruptionBudget.minAvailable }}
+ minAvailable: {{ .Values.data.podDisruptionBudget.minAvailable }}
+{{- end }}
+{{- if .Values.data.podDisruptionBudget.maxUnavailable }}
+ maxUnavailable: {{ .Values.data.podDisruptionBudget.maxUnavailable }}
+{{- end }}
+ selector:
+ matchLabels:
+ app: {{ template "elasticsearch.name" . }}
+ component: "{{ .Values.data.name }}"
+ release: {{ .Release.Name }}
+{{- end }}
diff --git a/manifests/es-cluster/elasticsearch/templates/data-statefulset.yaml b/manifests/es-cluster/elasticsearch/templates/data-statefulset.yaml
new file mode 100644
index 0000000..50aefe2
--- /dev/null
+++ b/manifests/es-cluster/elasticsearch/templates/data-statefulset.yaml
@@ -0,0 +1,190 @@
+apiVersion: apps/v1beta1
+kind: StatefulSet
+metadata:
+ labels:
+ app: {{ template "elasticsearch.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.data.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "elasticsearch.data.fullname" . }}
+spec:
+ serviceName: {{ template "elasticsearch.data.fullname" . }}
+ replicas: {{ .Values.data.replicas }}
+ template:
+ metadata:
+ labels:
+ app: {{ template "elasticsearch.name" . }}
+ component: "{{ .Values.data.name }}"
+ release: {{ .Release.Name }}
+ {{- if .Values.data.podAnnotations }}
+ annotations:
+{{ toYaml .Values.data.podAnnotations | indent 8 }}
+ {{- end }}
+ spec:
+{{- if .Values.data.priorityClassName }}
+ priorityClassName: "{{ .Values.data.priorityClassName }}"
+{{- end }}
+ securityContext:
+ fsGroup: 1000
+ {{- if eq .Values.data.antiAffinity "hard" }}
+ affinity:
+ podAntiAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - topologyKey: "kubernetes.io/hostname"
+ labelSelector:
+ matchLabels:
+ app: "{{ template "elasticsearch.name" . }}"
+ release: "{{ .Release.Name }}"
+ component: "{{ .Values.data.name }}"
+ {{- else if eq .Values.data.antiAffinity "soft" }}
+ affinity:
+ podAntiAffinity:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - weight: 1
+ podAffinityTerm:
+ topologyKey: kubernetes.io/hostname
+ labelSelector:
+ matchLabels:
+ app: "{{ template "elasticsearch.name" . }}"
+ release: "{{ .Release.Name }}"
+ component: "{{ .Values.data.name }}"
+ {{- end }}
+{{- if .Values.data.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.data.nodeSelector | indent 8 }}
+{{- end }}
+{{- if .Values.data.tolerations }}
+ tolerations:
+{{ toYaml .Values.data.tolerations | indent 8 }}
+{{- end }}
+ initContainers:
+ # see https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html
+ # and https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration-memory.html#mlockall
+ - name: "sysctl"
+ image: "busybox"
+ imagePullPolicy: "Always"
+ command: ["sysctl", "-w", "vm.max_map_count=262144"]
+ securityContext:
+ privileged: true
+ - name: "chown"
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
+ command:
+ - /bin/bash
+ - -c
+ - chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/data &&
+ chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/logs
+ securityContext:
+ runAsUser: 0
+ volumeMounts:
+ - mountPath: /usr/share/elasticsearch/data
+ name: data
+ containers:
+ - name: elasticsearch
+ env:
+ - name: DISCOVERY_SERVICE
+ value: {{ template "elasticsearch.fullname" . }}-discovery
+ - name: NODE_MASTER
+ value: "false"
+ - name: PROCESSORS
+ valueFrom:
+ resourceFieldRef:
+ resource: limits.cpu
+ - name: ES_JAVA_OPTS
+ value: "-Djava.net.preferIPv4Stack=true -Xms{{ .Values.data.heapSize }} -Xmx{{ .Values.data.heapSize }}"
+ {{- range $key, $value := .Values.cluster.env }}
+ - name: {{ $key }}
+ value: {{ $value | quote }}
+ {{- end }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
+ ports:
+ - containerPort: 9300
+ name: transport
+{{ if .Values.data.exposeHttp }}
+ - containerPort: 9200
+ name: http
+{{ end }}
+ resources:
+{{ toYaml .Values.data.resources | indent 12 }}
+ readinessProbe:
+ httpGet:
+ path: /_cluster/health?local=true
+ port: 9200
+ initialDelaySeconds: 5
+ volumeMounts:
+ - mountPath: /usr/share/elasticsearch/data
+ name: data
+ - mountPath: /usr/share/elasticsearch/config/elasticsearch.yml
+ name: config
+ subPath: elasticsearch.yml
+{{- if hasPrefix "2." .Values.image.tag }}
+ - mountPath: /usr/share/elasticsearch/config/logging.yml
+ name: config
+ subPath: logging.yml
+{{- end }}
+{{- if hasPrefix "5." .Values.image.tag }}
+ - mountPath: /usr/share/elasticsearch/config/log4j2.properties
+ name: config
+ subPath: log4j2.properties
+{{- end }}
+ - name: config
+ mountPath: /pre-stop-hook.sh
+ subPath: pre-stop-hook.sh
+ - name: config
+ mountPath: /post-start-hook.sh
+ subPath: post-start-hook.sh
+{{- if .Values.cluster.keystoreSecret }}
+ - name: keystore
+ mountPath: "/usr/share/elasticsearch/config/elasticsearch.keystore"
+ subPath: elasticsearch.keystore
+ readOnly: true
+{{- end }}
+ lifecycle:
+ preStop:
+ exec:
+ command: ["/bin/bash","/pre-stop-hook.sh"]
+ postStart:
+ exec:
+ command: ["/bin/bash","/post-start-hook.sh"]
+ terminationGracePeriodSeconds: {{ .Values.data.terminationGracePeriodSeconds }}
+{{- if .Values.image.pullSecrets }}
+ imagePullSecrets:
+ {{- range $pullSecret := .Values.image.pullSecrets }}
+ - name: {{ $pullSecret }}
+ {{- end }}
+{{- end }}
+ volumes:
+ - name: config
+ configMap:
+ name: {{ template "elasticsearch.fullname" . }}
+{{- if .Values.cluster.keystoreSecret }}
+ - name: keystore
+ secret:
+ secretName: {{ .Values.cluster.keystoreSecret }}
+{{- end }}
+ {{- if not .Values.data.persistence.enabled }}
+ - name: data
+ emptyDir: {}
+ {{- end }}
+ updateStrategy:
+ type: {{ .Values.data.updateStrategy.type }}
+ {{- if .Values.data.persistence.enabled }}
+ volumeClaimTemplates:
+ - metadata:
+ name: {{ .Values.data.persistence.name }}
+ spec:
+ accessModes:
+ - {{ .Values.data.persistence.accessMode | quote }}
+ {{- if .Values.data.persistence.storageClass }}
+ {{- if (eq "-" .Values.data.persistence.storageClass) }}
+ storageClassName: ""
+ {{- else }}
+ storageClassName: "{{ .Values.data.persistence.storageClass }}"
+ {{- end }}
+ {{- end }}
+ resources:
+ requests:
+ storage: "{{ .Values.data.persistence.size }}"
+ {{- end }}
diff --git a/manifests/es-cluster/elasticsearch/templates/master-pdb.yaml b/manifests/es-cluster/elasticsearch/templates/master-pdb.yaml
new file mode 100644
index 0000000..c3efe83
--- /dev/null
+++ b/manifests/es-cluster/elasticsearch/templates/master-pdb.yaml
@@ -0,0 +1,24 @@
+{{- if .Values.master.podDisruptionBudget.enabled }}
+apiVersion: policy/v1beta1
+kind: PodDisruptionBudget
+metadata:
+ labels:
+ app: {{ template "elasticsearch.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.master.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "elasticsearch.master.fullname" . }}
+spec:
+{{- if .Values.master.podDisruptionBudget.minAvailable }}
+ minAvailable: {{ .Values.master.podDisruptionBudget.minAvailable }}
+{{- end }}
+{{- if .Values.master.podDisruptionBudget.maxUnavailable }}
+ maxUnavailable: {{ .Values.master.podDisruptionBudget.maxUnavailable }}
+{{- end }}
+ selector:
+ matchLabels:
+ app: {{ template "elasticsearch.name" . }}
+ component: "{{ .Values.master.name }}"
+ release: {{ .Release.Name }}
+{{- end }}
diff --git a/manifests/es-cluster/elasticsearch/templates/master-statefulset.yaml b/manifests/es-cluster/elasticsearch/templates/master-statefulset.yaml
new file mode 100644
index 0000000..feb3159
--- /dev/null
+++ b/manifests/es-cluster/elasticsearch/templates/master-statefulset.yaml
@@ -0,0 +1,180 @@
+apiVersion: apps/v1beta1
+kind: StatefulSet
+metadata:
+ labels:
+ app: {{ template "elasticsearch.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.master.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "elasticsearch.master.fullname" . }}
+spec:
+ serviceName: {{ template "elasticsearch.master.fullname" . }}
+ replicas: {{ .Values.master.replicas }}
+ template:
+ metadata:
+ labels:
+ app: {{ template "elasticsearch.name" . }}
+ component: "{{ .Values.master.name }}"
+ release: {{ .Release.Name }}
+ {{- if .Values.master.podAnnotations }}
+ annotations:
+{{ toYaml .Values.master.podAnnotations | indent 8 }}
+ {{- end }}
+ spec:
+{{- if .Values.master.priorityClassName }}
+ priorityClassName: "{{ .Values.master.priorityClassName }}"
+{{- end }}
+ securityContext:
+ fsGroup: 1000
+ {{- if eq .Values.master.antiAffinity "hard" }}
+ affinity:
+ podAntiAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - topologyKey: "kubernetes.io/hostname"
+ labelSelector:
+ matchLabels:
+ app: "{{ template "elasticsearch.name" . }}"
+ release: "{{ .Release.Name }}"
+ component: "{{ .Values.master.name }}"
+ {{- else if eq .Values.master.antiAffinity "soft" }}
+ affinity:
+ podAntiAffinity:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - weight: 1
+ podAffinityTerm:
+ topologyKey: kubernetes.io/hostname
+ labelSelector:
+ matchLabels:
+ app: "{{ template "elasticsearch.name" . }}"
+ release: "{{ .Release.Name }}"
+ component: "{{ .Values.master.name }}"
+ {{- end }}
+{{- if .Values.master.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.master.nodeSelector | indent 8 }}
+{{- end }}
+{{- if .Values.master.tolerations }}
+ tolerations:
+{{ toYaml .Values.master.tolerations | indent 8 }}
+{{- end }}
+ initContainers:
+ # see https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html
+ # and https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration-memory.html#mlockall
+ - name: "sysctl"
+ image: "busybox"
+ imagePullPolicy: "Always"
+ command: ["sysctl", "-w", "vm.max_map_count=262144"]
+ securityContext:
+ privileged: true
+ - name: "chown"
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
+ command:
+ - /bin/bash
+ - -c
+ - chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/data &&
+ chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/logs
+ securityContext:
+ runAsUser: 0
+ volumeMounts:
+ - mountPath: /usr/share/elasticsearch/data
+ name: data
+ containers:
+ - name: elasticsearch
+ env:
+ - name: NODE_DATA
+ value: "false"
+{{- if hasPrefix "5." .Values.appVersion }}
+ - name: NODE_INGEST
+ value: "false"
+{{- end }}
+ - name: DISCOVERY_SERVICE
+ value: {{ template "elasticsearch.fullname" . }}-discovery
+ - name: PROCESSORS
+ valueFrom:
+ resourceFieldRef:
+ resource: limits.cpu
+ - name: ES_JAVA_OPTS
+ value: "-Djava.net.preferIPv4Stack=true -Xms{{ .Values.master.heapSize }} -Xmx{{ .Values.master.heapSize }}"
+ {{- range $key, $value := .Values.cluster.env }}
+ - name: {{ $key }}
+ value: {{ $value | quote }}
+ {{- end }}
+ resources:
+{{ toYaml .Values.master.resources | indent 12 }}
+ readinessProbe:
+ httpGet:
+ path: /_cluster/health?local=true
+ port: 9200
+ initialDelaySeconds: 5
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
+ ports:
+ - containerPort: 9300
+ name: transport
+{{ if .Values.master.exposeHttp }}
+ - containerPort: 9200
+ name: http
+{{ end }}
+ volumeMounts:
+ - mountPath: /usr/share/elasticsearch/data
+ name: data
+ - mountPath: /usr/share/elasticsearch/config/elasticsearch.yml
+ name: config
+ subPath: elasticsearch.yml
+{{- if hasPrefix "2." .Values.image.tag }}
+ - mountPath: /usr/share/elasticsearch/config/logging.yml
+ name: config
+ subPath: logging.yml
+{{- end }}
+{{- if hasPrefix "5." .Values.image.tag }}
+ - mountPath: /usr/share/elasticsearch/config/log4j2.properties
+ name: config
+ subPath: log4j2.properties
+{{- end }}
+{{- if .Values.cluster.keystoreSecret }}
+ - name: keystore
+ mountPath: "/usr/share/elasticsearch/config/elasticsearch.keystore"
+ subPath: elasticsearch.keystore
+ readOnly: true
+{{- end }}
+{{- if .Values.image.pullSecrets }}
+ imagePullSecrets:
+ {{- range $pullSecret := .Values.image.pullSecrets }}
+ - name: {{ $pullSecret }}
+ {{- end }}
+{{- end }}
+ volumes:
+ - name: config
+ configMap:
+ name: {{ template "elasticsearch.fullname" . }}
+{{- if .Values.cluster.keystoreSecret }}
+ - name: keystore
+ secret:
+ secretName: {{ .Values.cluster.keystoreSecret }}
+{{- end }}
+ {{- if not .Values.master.persistence.enabled }}
+ - name: data
+ emptyDir: {}
+ {{- end }}
+ updateStrategy:
+ type: {{ .Values.master.updateStrategy.type }}
+ {{- if .Values.master.persistence.enabled }}
+ volumeClaimTemplates:
+ - metadata:
+ name: {{ .Values.master.persistence.name }}
+ spec:
+ accessModes:
+ - {{ .Values.master.persistence.accessMode | quote }}
+ {{- if .Values.master.persistence.storageClass }}
+ {{- if (eq "-" .Values.master.persistence.storageClass) }}
+ storageClassName: ""
+ {{- else }}
+ storageClassName: "{{ .Values.master.persistence.storageClass }}"
+ {{- end }}
+ {{- end }}
+ resources:
+ requests:
+ storage: "{{ .Values.master.persistence.size }}"
+ {{ end }}
diff --git a/manifests/es-cluster/elasticsearch/templates/master-svc.yaml b/manifests/es-cluster/elasticsearch/templates/master-svc.yaml
new file mode 100644
index 0000000..5db28b7
--- /dev/null
+++ b/manifests/es-cluster/elasticsearch/templates/master-svc.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app: {{ template "elasticsearch.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.master.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "elasticsearch.fullname" . }}-discovery
+spec:
+ clusterIP: None
+ ports:
+ - port: 9300
+ targetPort: transport
+ selector:
+ app: {{ template "elasticsearch.name" . }}
+ component: "{{ .Values.master.name }}"
+ release: {{ .Release.Name }}
diff --git a/manifests/es-cluster/elasticsearch/values.yaml b/manifests/es-cluster/elasticsearch/values.yaml
new file mode 100644
index 0000000..c1d2577
--- /dev/null
+++ b/manifests/es-cluster/elasticsearch/values.yaml
@@ -0,0 +1,121 @@
+# Default values for elasticsearch.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+appVersion: "6.4.0"
+
+image:
+ repository: "docker.elastic.co/elasticsearch/elasticsearch-oss"
+ tag: "6.4.0"
+ pullPolicy: "IfNotPresent"
+ # If specified, use these secrets to access the image
+ # pullSecrets:
+ # - registry-secret
+
+cluster:
+ name: "elasticsearch"
+ # If you want X-Pack installed, switch to an image that includes it, enable this option and toggle the features you want
+ # enabled in the environment variables outlined in the README
+ xpackEnable: false
+ # Some settings must be placed in a keystore, so they need to be mounted in from a secret.
+ # Use this setting to specify the name of the secret
+ # keystoreSecret: eskeystore
+ config: {}
+ env:
+ # IMPORTANT: https://www.elastic.co/guide/en/elasticsearch/reference/current/important-settings.html#minimum_master_nodes
+ # To prevent data loss, it is vital to configure the discovery.zen.minimum_master_nodes setting so that each master-eligible
+ # node knows the minimum number of master-eligible nodes that must be visible in order to form a cluster.
+ MINIMUM_MASTER_NODES: "2"
+
+client:
+ name: client
+ replicas: 2
+ serviceType: ClusterIP
+ loadBalancerIP: {}
+ loadBalancerSourceRanges: {}
+## (dict) If specified, apply these annotations to the client service
+# serviceAnnotations:
+# example: client-svc-foo
+ heapSize: "512m"
+ antiAffinity: "soft"
+ nodeSelector: {}
+ tolerations: []
+ resources:
+ limits:
+ cpu: "1"
+ # memory: "1024Mi"
+ requests:
+ cpu: "25m"
+ memory: "512Mi"
+ priorityClassName: ""
+ ## (dict) If specified, apply these annotations to each client Pod
+ # podAnnotations:
+ # example: client-foo
+ podDisruptionBudget:
+ enabled: false
+ minAvailable: 1
+ # maxUnavailable: 1
+
+master:
+ name: master
+ exposeHttp: false
+ replicas: 3
+ heapSize: "512m"
+ persistence:
+ enabled: true
+ accessMode: ReadWriteOnce
+ name: data
+ size: "4Gi"
+ # storageClass: "ssd"
+ antiAffinity: "soft"
+ nodeSelector: {}
+ tolerations: []
+ resources:
+ limits:
+ cpu: "1"
+ # memory: "1024Mi"
+ requests:
+ cpu: "25m"
+ memory: "512Mi"
+ priorityClassName: ""
+ ## (dict) If specified, apply these annotations to each master Pod
+ # podAnnotations:
+ # example: master-foo
+ podDisruptionBudget:
+ enabled: false
+ minAvailable: 2 # Same as `cluster.env.MINIMUM_MASTER_NODES`
+ # maxUnavailable: 1
+ updateStrategy:
+ type: OnDelete
+
+data:
+ name: data
+ exposeHttp: false
+ replicas: 2
+ heapSize: "1536m"
+ persistence:
+ enabled: true
+ accessMode: ReadWriteOnce
+ name: data
+ size: "30Gi"
+ # storageClass: "ssd"
+ terminationGracePeriodSeconds: 3600
+ antiAffinity: "soft"
+ nodeSelector: {}
+ tolerations: []
+ resources:
+ limits:
+ cpu: "1"
+ # memory: "2048Mi"
+ requests:
+ cpu: "25m"
+ memory: "1536Mi"
+ priorityClassName: ""
+ ## (dict) If specified, apply these annotations to each data Pod
+ # podAnnotations:
+ # example: data-foo
+ podDisruptionBudget:
+ enabled: false
+ # minAvailable: 1
+ maxUnavailable: 1
+ updateStrategy:
+ type: OnDelete
diff --git a/manifests/es-cluster/es-values.yaml b/manifests/es-cluster/es-values.yaml
new file mode 100644
index 0000000..2a92d3f
--- /dev/null
+++ b/manifests/es-cluster/es-values.yaml
@@ -0,0 +1,44 @@
+image:
+ repository: "jmgao1983/elasticsearch"
+
+cluster:
+ name: "es-on-k8s"
+ env:
+ MINIMUM_MASTER_NODES: "2"
+
+client:
+ serviceType: NodePort
+
+master:
+ name: master
+ replicas: 3
+ heapSize: "512m"
+ persistence:
+ enabled: true
+ accessMode: ReadWriteOnce
+ name: data
+ size: "4Gi"
+ storageClass: "nfs-es"
+
+data:
+ name: data
+ replicas: 2
+ heapSize: "1536m"
+ persistence:
+ enabled: true
+ accessMode: ReadWriteOnce
+ name: data
+ size: "40Gi"
+ storageClass: "nfs-es"
+ terminationGracePeriodSeconds: 3600
+ resources:
+ limits:
+ cpu: "1"
+ # memory: "2048Mi"
+ requests:
+ cpu: "25m"
+ memory: "1536Mi"
+ podDisruptionBudget:
+ enabled: false
+ # minAvailable: 1
+ maxUnavailable: 1
diff --git a/manifests/heapster/grafana.yaml b/manifests/heapster/grafana.yaml
new file mode 100644
index 0000000..5772420
--- /dev/null
+++ b/manifests/heapster/grafana.yaml
@@ -0,0 +1,71 @@
+---
+apiVersion: apps/v1beta1
+kind: Deployment
+metadata:
+ name: monitoring-grafana
+ namespace: kube-system
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: grafana
+ template:
+ metadata:
+ labels:
+ task: monitoring
+ k8s-app: grafana
+ spec:
+ containers:
+ - name: grafana
+ #image: gcr.io/google_containers/heapster-grafana-amd64:v4.2.0
+ image: mirrorgooglecontainers/heapster-grafana-amd64:v4.4.3
+ ports:
+ - containerPort: 3000
+ protocol: TCP
+ volumeMounts:
+ - mountPath: /var
+ name: grafana-storage
+ env:
+ - name: INFLUXDB_HOST
+ value: monitoring-influxdb
+ - name: GF_SERVER_HTTP_PORT
+ value: "3000"
+ # The following env variables are required to make Grafana accessible via
+ # the kubernetes api-server proxy. On production clusters, we recommend
+ # removing these env variables, setup auth for grafana, and expose the grafana
+ # service using a LoadBalancer or a public IP.
+ - name: GF_AUTH_BASIC_ENABLED
+ value: "false"
+ - name: GF_AUTH_ANONYMOUS_ENABLED
+ value: "true"
+ - name: GF_AUTH_ANONYMOUS_ORG_ROLE
+ value: Admin
+ - name: GF_SERVER_ROOT_URL
+ # If you're only using the API Server proxy, set this value instead:
+ value: /api/v1/namespaces/kube-system/services/monitoring-grafana/proxy/
+ #value: /
+ volumes:
+ - name: grafana-storage
+ emptyDir: {}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ # For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
+ # If you are NOT using this as an addon, you should comment out this line.
+ kubernetes.io/cluster-service: 'true'
+ kubernetes.io/name: monitoring-grafana
+ name: monitoring-grafana
+ namespace: kube-system
+spec:
+ # In a production setup, we recommend accessing Grafana through an external Loadbalancer
+ # or through a public IP.
+ # type: LoadBalancer
+ # You could also use NodePort to expose the service at a randomly-generated port
+ # type: NodePort
+ ports:
+ - port: 80
+ targetPort: 3000
+ selector:
+ k8s-app: grafana
diff --git a/manifests/heapster/heapster.yaml b/manifests/heapster/heapster.yaml
new file mode 100644
index 0000000..534bcd4
--- /dev/null
+++ b/manifests/heapster/heapster.yaml
@@ -0,0 +1,74 @@
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: heapster
+ namespace: kube-system
+---
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: heapster
+subjects:
+ - kind: ServiceAccount
+ name: heapster
+ namespace: kube-system
+roleRef:
+ kind: ClusterRole
+ name: system:heapster
+ apiGroup: rbac.authorization.k8s.io
+---
+
+apiVersion: apps/v1beta1
+kind: Deployment
+metadata:
+ name: heapster
+ namespace: kube-system
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: heapster
+ template:
+ metadata:
+ labels:
+ task: monitoring
+ k8s-app: heapster
+ spec:
+ serviceAccountName: heapster
+ containers:
+ - name: heapster
+ #image: gcr.io/google_containers/heapster-amd64:v1.5.4
+ image: mirrorgooglecontainers/heapster-amd64:v1.5.4
+ imagePullPolicy: IfNotPresent
+ command:
+ - /heapster
+ #- --source=kubernetes:https://kubernetes.default
+ - --source=kubernetes.summary_api:''
+ - --sink=influxdb:http://monitoring-influxdb.kube-system.svc:8086
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8082
+ scheme: HTTP
+ initialDelaySeconds: 180
+ timeoutSeconds: 5
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ task: monitoring
+ # For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
+ # If you are NOT using this as an addon, you should comment out this line.
+ #kubernetes.io/cluster-service: 'true'
+ kubernetes.io/name: Heapster
+ name: heapster
+ namespace: kube-system
+spec:
+ ports:
+ - port: 80
+ targetPort: 8082
+ selector:
+ k8s-app: heapster
diff --git a/manifests/heapster/influxdb-v1.1.1/influxdb.yaml b/manifests/heapster/influxdb-v1.1.1/influxdb.yaml
new file mode 100644
index 0000000..8d93a04
--- /dev/null
+++ b/manifests/heapster/influxdb-v1.1.1/influxdb.yaml
@@ -0,0 +1,190 @@
+---
+apiVersion: apps/v1beta1
+kind: Deployment
+metadata:
+ name: monitoring-influxdb
+ namespace: kube-system
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: influxdb
+ template:
+ metadata:
+ labels:
+ task: monitoring
+ k8s-app: influxdb
+ spec:
+ containers:
+ - name: influxdb
+ #image: gcr.io/google_containers/heapster-influxdb-amd64:v1.1.1
+ image: mirrorgooglecontainers/heapster-influxdb-amd64:v1.1.1
+ volumeMounts:
+ - mountPath: /data
+ name: influxdb-storage
+ - mountPath: /etc/
+ name: influxdb-config
+ volumes:
+ - name: influxdb-storage
+ emptyDir: {}
+ - name: influxdb-config
+ configMap:
+ name: influxdb-config
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ task: monitoring
+ # For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
+ # If you are NOT using this as an addon, you should comment out this line.
+ # kubernetes.io/cluster-service: 'true'
+ kubernetes.io/name: monitoring-influxdb
+ name: monitoring-influxdb
+ namespace: kube-system
+spec:
+ type: NodePort
+ ports:
+ - port: 8086
+ targetPort: 8086
+ name: http
+ - port: 8083
+ targetPort: 8083
+ name: admin
+ selector:
+ k8s-app: influxdb
+---
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: influxdb-config
+ namespace: kube-system
+data:
+ config.toml: |
+ reporting-disabled = true
+ bind-address = ":8088"
+
+ [meta]
+ dir = "/data/meta"
+ retention-autocreate = true
+ logging-enabled = true
+
+ [data]
+ dir = "/data/data"
+ wal-dir = "/data/wal"
+ query-log-enabled = true
+ cache-max-memory-size = 1073741824
+ cache-snapshot-memory-size = 26214400
+ cache-snapshot-write-cold-duration = "10m0s"
+ compact-full-write-cold-duration = "4h0m0s"
+ max-series-per-database = 1000000
+ max-values-per-tag = 100000
+ trace-logging-enabled = false
+
+ [coordinator]
+ write-timeout = "10s"
+ max-concurrent-queries = 0
+ query-timeout = "0s"
+ log-queries-after = "0s"
+ max-select-point = 0
+ max-select-series = 0
+ max-select-buckets = 0
+
+ [retention]
+ enabled = true
+ check-interval = "30m0s"
+
+ [admin]
+ enabled = true
+ bind-address = ":8083"
+ https-enabled = false
+ https-certificate = "/etc/ssl/influxdb.pem"
+
+ [shard-precreation]
+ enabled = true
+ check-interval = "10m0s"
+ advance-period = "30m0s"
+
+ [monitor]
+ store-enabled = true
+ store-database = "_internal"
+ store-interval = "10s"
+
+ [subscriber]
+ enabled = true
+ http-timeout = "30s"
+ insecure-skip-verify = false
+ ca-certs = ""
+ write-concurrency = 40
+ write-buffer-size = 1000
+
+ [http]
+ enabled = true
+ bind-address = ":8086"
+ auth-enabled = false
+ log-enabled = true
+ write-tracing = false
+ pprof-enabled = false
+ https-enabled = false
+ https-certificate = "/etc/ssl/influxdb.pem"
+ https-private-key = ""
+ max-row-limit = 10000
+ max-connection-limit = 0
+ shared-secret = ""
+ realm = "InfluxDB"
+ unix-socket-enabled = false
+ bind-socket = "/var/run/influxdb.sock"
+
+ [[graphite]]
+ enabled = false
+ bind-address = ":2003"
+ database = "graphite"
+ retention-policy = ""
+ protocol = "tcp"
+ batch-size = 5000
+ batch-pending = 10
+ batch-timeout = "1s"
+ consistency-level = "one"
+ separator = "."
+ udp-read-buffer = 0
+
+ [[collectd]]
+ enabled = false
+ bind-address = ":25826"
+ database = "collectd"
+ retention-policy = ""
+ batch-size = 5000
+ batch-pending = 10
+ batch-timeout = "10s"
+ read-buffer = 0
+ typesdb = "/usr/share/collectd/types.db"
+
+ [[opentsdb]]
+ enabled = false
+ bind-address = ":4242"
+ database = "opentsdb"
+ retention-policy = ""
+ consistency-level = "one"
+ tls-enabled = false
+ certificate = "/etc/ssl/influxdb.pem"
+ batch-size = 1000
+ batch-pending = 5
+ batch-timeout = "1s"
+ log-point-errors = true
+
+ [[udp]]
+ enabled = false
+ bind-address = ":8089"
+ database = "udp"
+ retention-policy = ""
+ batch-size = 5000
+ batch-pending = 10
+ read-buffer = 0
+ batch-timeout = "1s"
+ precision = ""
+
+ [continuous_queries]
+ log-enabled = true
+ enabled = true
+ run-interval = "1s"
diff --git a/manifests/heapster/influxdb-with-pv/influxdb-pv.yaml b/manifests/heapster/influxdb-with-pv/influxdb-pv.yaml
new file mode 100644
index 0000000..5059075
--- /dev/null
+++ b/manifests/heapster/influxdb-with-pv/influxdb-pv.yaml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: PersistentVolume
+metadata:
+ name: pv-influxdb
+spec:
+ capacity:
+ storage: 5Gi
+ accessModes:
+ - ReadWriteMany
+ volumeMode: Filesystem
+ persistentVolumeReclaimPolicy: Recycle
+ storageClassName: slow
+ nfs:
+ # 根据实际共享目录修改
+ path: /share
+ # 根据实际 nfs服务器地址修改
+ server: 192.168.1.208
diff --git a/manifests/heapster/influxdb-with-pv/influxdb-pvc.yaml b/manifests/heapster/influxdb-with-pv/influxdb-pvc.yaml
new file mode 100644
index 0000000..8e10a6f
--- /dev/null
+++ b/manifests/heapster/influxdb-with-pv/influxdb-pvc.yaml
@@ -0,0 +1,19 @@
+---
+kind: PersistentVolumeClaim
+apiVersion: v1
+metadata:
+ name: influxdb-claim
+ namespace: kube-system
+spec:
+ accessModes:
+ - ReadWriteMany
+ volumeMode: Filesystem
+ resources:
+ requests:
+ storage: 3Gi
+ storageClassName: slow
+ #selector:
+ # matchLabels:
+ # release: "stable"
+ # matchExpressions:
+ # - {key: environment, operator: In, values: [dev]}
diff --git a/manifests/heapster/influxdb-with-pv/influxdb.yaml b/manifests/heapster/influxdb-with-pv/influxdb.yaml
new file mode 100644
index 0000000..aecddef
--- /dev/null
+++ b/manifests/heapster/influxdb-with-pv/influxdb.yaml
@@ -0,0 +1,47 @@
+---
+apiVersion: apps/v1beta1
+kind: Deployment
+metadata:
+ name: monitoring-influxdb
+ namespace: kube-system
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: influxdb
+ template:
+ metadata:
+ labels:
+ task: monitoring
+ k8s-app: influxdb
+ spec:
+ containers:
+ - name: influxdb
+ #image: gcr.io/google_containers/heapster-influxdb-amd64:v1.3.3
+ image: mirrorgooglecontainers/heapster-influxdb-amd64:v1.3.3
+ volumeMounts:
+ - mountPath: /data
+ name: influxdb-storage
+ volumes:
+ - name: influxdb-storage
+ persistentVolumeClaim:
+ claimName: influxdb-claim
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ task: monitoring
+ # For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
+ # If you are NOT using this as an addon, you should comment out this line.
+ # kubernetes.io/cluster-service: 'true'
+ kubernetes.io/name: monitoring-influxdb
+ name: monitoring-influxdb
+ namespace: kube-system
+spec:
+ ports:
+ - port: 8086
+ targetPort: 8086
+ name: http
+ selector:
+ k8s-app: influxdb
diff --git a/manifests/heapster/influxdb.yaml b/manifests/heapster/influxdb.yaml
new file mode 100644
index 0000000..14a95a2
--- /dev/null
+++ b/manifests/heapster/influxdb.yaml
@@ -0,0 +1,48 @@
+---
+apiVersion: apps/v1beta1
+kind: Deployment
+metadata:
+ name: monitoring-influxdb
+ namespace: kube-system
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: influxdb
+ template:
+ metadata:
+ labels:
+ task: monitoring
+ k8s-app: influxdb
+ spec:
+ containers:
+ - name: influxdb
+ #image: gcr.io/google_containers/heapster-influxdb-amd64:v1.3.3
+ image: mirrorgooglecontainers/heapster-influxdb-amd64:v1.3.3
+ volumeMounts:
+ - mountPath: /data
+ name: influxdb-storage
+ volumes:
+ - name: influxdb-storage
+ emptyDir: {}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ task: monitoring
+ # For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
+ # If you are NOT using this as an addon, you should comment out this line.
+ # kubernetes.io/cluster-service: 'true'
+ kubernetes.io/name: monitoring-influxdb
+ name: monitoring-influxdb
+ namespace: kube-system
+spec:
+ ports:
+ - port: 8086
+ targetPort: 8086
+ name: http
+ selector:
+ k8s-app: influxdb
+---
+
diff --git a/manifests/ingress/nginx-ingress/nginx-ingress-svc.yaml b/manifests/ingress/nginx-ingress/nginx-ingress-svc.yaml
new file mode 100644
index 0000000..6acd9bc
--- /dev/null
+++ b/manifests/ingress/nginx-ingress/nginx-ingress-svc.yaml
@@ -0,0 +1,44 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: ingress-nginx
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+spec:
+ type: NodePort
+ ports:
+ - name: http
+ port: 80
+ targetPort: 80
+ protocol: TCP
+ # 集群hosts文件中设置的 NODE_PORT_RANGE 作为 NodePort的可用范围
+ # 从默认20000~40000之间选一个可用端口,让ingress-controller暴露给外部的访问
+ nodePort: 23456
+ - name: https
+ port: 443
+ targetPort: 443
+ protocol: TCP
+ # 集群hosts文件中设置的 NODE_PORT_RANGE 作为 NodePort的可用范围
+ # 从默认20000~40000之间选一个可用端口,让ingress-controller暴露https
+ nodePort: 23457
+ - name: test-mysql
+ port: 3306
+ targetPort: 3306
+ protocol: TCP
+ nodePort: 23306
+ - name: test-mysql-read
+ port: 3307
+ targetPort: 3307
+ protocol: TCP
+ nodePort: 23307
+ - name: test-dns
+ port: 53
+ targetPort: 53
+ protocol: UDP
+ nodePort: 20053
+ selector:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+
diff --git a/manifests/ingress/nginx-ingress/nginx-ingress.yaml b/manifests/ingress/nginx-ingress/nginx-ingress.yaml
new file mode 100644
index 0000000..48bb0fc
--- /dev/null
+++ b/manifests/ingress/nginx-ingress/nginx-ingress.yaml
@@ -0,0 +1,302 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: ingress-nginx
+
+---
+
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: nginx-configuration
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+
+---
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: tcp-services
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+
+---
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: udp-services
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: nginx-ingress-serviceaccount
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+ name: nginx-ingress-clusterrole
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ - endpoints
+ - nodes
+ - pods
+ - secrets
+ verbs:
+ - list
+ - watch
+ - apiGroups:
+ - ""
+ resources:
+ - nodes
+ verbs:
+ - get
+ - apiGroups:
+ - ""
+ resources:
+ - services
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - "extensions"
+ resources:
+ - ingresses
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+ - apiGroups:
+ - "extensions"
+ resources:
+ - ingresses/status
+ verbs:
+ - update
+
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: Role
+metadata:
+ name: nginx-ingress-role
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ - pods
+ - secrets
+ - namespaces
+ verbs:
+ - get
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ resourceNames:
+ # Defaults to "-"
+ # Here: "-"
+ # This has to be adapted if you change either parameter
+ # when launching the nginx-ingress-controller.
+ - "ingress-controller-leader-nginx"
+ verbs:
+ - get
+ - update
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - create
+ - apiGroups:
+ - ""
+ resources:
+ - endpoints
+ verbs:
+ - get
+
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: RoleBinding
+metadata:
+ name: nginx-ingress-role-nisa-binding
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: nginx-ingress-role
+subjects:
+ - kind: ServiceAccount
+ name: nginx-ingress-serviceaccount
+ namespace: ingress-nginx
+
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ name: nginx-ingress-clusterrole-nisa-binding
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: nginx-ingress-clusterrole
+subjects:
+ - kind: ServiceAccount
+ name: nginx-ingress-serviceaccount
+ namespace: ingress-nginx
+
+---
+
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: nginx-ingress-controller
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+ annotations:
+ prometheus.io/port: "10254"
+ prometheus.io/scrape: "true"
+ spec:
+ serviceAccountName: nginx-ingress-serviceaccount
+ containers:
+ - name: nginx-ingress-controller
+ #image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.21.0
+ #使用以下镜像,方便国内下载加速
+ image: jmgao1983/nginx-ingress-controller:0.21.0
+ args:
+ - /nginx-ingress-controller
+ - --configmap=$(POD_NAMESPACE)/nginx-configuration
+ - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
+ - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
+ - --publish-service=$(POD_NAMESPACE)/ingress-nginx
+ - --annotations-prefix=nginx.ingress.kubernetes.io
+ securityContext:
+ capabilities:
+ drop:
+ - ALL
+ add:
+ - NET_BIND_SERVICE
+ # www-data -> 33
+ runAsUser: 33
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ ports:
+ - name: http
+ containerPort: 80
+ - name: https
+ containerPort: 443
+ # hostPort可以直接使用node节点的网络端口暴露服务
+ #- name: mysql
+ # containerPort: 3306
+ # hostPort: 3306
+ #- name: dns
+ # containerPort: 53
+ # hostPort: 53
+ # protocol: UDP
+ livenessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /healthz
+ port: 10254
+ scheme: HTTP
+ initialDelaySeconds: 10
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 1
+ readinessProbe:
+ failureThreshold: 3
+ httpGet:
+ path: /healthz
+ port: 10254
+ scheme: HTTP
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 1
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: ingress-nginx
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+spec:
+ type: NodePort
+ ports:
+ - name: http
+ port: 80
+ targetPort: 80
+ protocol: TCP
+ # 集群hosts文件中设置的 NODE_PORT_RANGE 作为 NodePort的可用范围
+ # 从默认20000~40000之间选一个可用端口,让ingress-controller暴露给外部的访问
+ nodePort: 23456
+ - name: https
+ port: 443
+ targetPort: 443
+ protocol: TCP
+ # 集群hosts文件中设置的 NODE_PORT_RANGE 作为 NodePort的可用范围
+ # 从默认20000~40000之间选一个可用端口,让ingress-controller暴露https
+ nodePort: 23457
+ selector:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+
+---
+
diff --git a/manifests/ingress/nginx-ingress/tcp-services-configmap.yaml b/manifests/ingress/nginx-ingress/tcp-services-configmap.yaml
new file mode 100644
index 0000000..3a97132
--- /dev/null
+++ b/manifests/ingress/nginx-ingress/tcp-services-configmap.yaml
@@ -0,0 +1,12 @@
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: tcp-services
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+data:
+ 3306: "mariadb/mydb-mariadb:3306"
+ 3307: "mariadb/mydb-mariadb-slave:3306"
+
diff --git a/manifests/ingress/nginx-ingress/udp-services-configmap.yaml b/manifests/ingress/nginx-ingress/udp-services-configmap.yaml
new file mode 100644
index 0000000..d75383f
--- /dev/null
+++ b/manifests/ingress/nginx-ingress/udp-services-configmap.yaml
@@ -0,0 +1,11 @@
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: udp-services
+ namespace: ingress-nginx
+ labels:
+ app.kubernetes.io/name: ingress-nginx
+ app.kubernetes.io/part-of: ingress-nginx
+data:
+ 53: "kube-system/kube-dns:53"
+
diff --git a/manifests/ingress/test-hello.ing.yaml b/manifests/ingress/test-hello.ing.yaml
new file mode 100644
index 0000000..9ea0854
--- /dev/null
+++ b/manifests/ingress/test-hello.ing.yaml
@@ -0,0 +1,14 @@
+# kubectl run test-hello --image=nginx --expose --port=80
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: test-hello
+spec:
+ rules:
+ - host: hello.test.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: test-hello
+ servicePort: 80
diff --git a/manifests/ingress/traefik/tls/hello-tls.ing.yaml b/manifests/ingress/traefik/tls/hello-tls.ing.yaml
new file mode 100644
index 0000000..fe27777
--- /dev/null
+++ b/manifests/ingress/traefik/tls/hello-tls.ing.yaml
@@ -0,0 +1,16 @@
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: hello-tls-ingress
+ annotations:
+ kubernetes.io/ingress.class: traefik
+spec:
+ rules:
+ - host: hello.test.com
+ http:
+ paths:
+ - backend:
+ serviceName: test-hello
+ servicePort: 80
+ tls:
+ - secretName: traefik-cert
diff --git a/manifests/ingress/traefik/tls/k8s-dashboard.ing.yaml b/manifests/ingress/traefik/tls/k8s-dashboard.ing.yaml
new file mode 100644
index 0000000..8c9d2a3
--- /dev/null
+++ b/manifests/ingress/traefik/tls/k8s-dashboard.ing.yaml
@@ -0,0 +1,17 @@
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: kubernetes-dashboard
+ namespace: kube-system
+ annotations:
+ traefik.ingress.kubernetes.io/redirect-entry-point: https
+spec:
+ rules:
+ - host: dashboard.test.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: kubernetes-dashboard
+ servicePort: 443
+
diff --git a/manifests/ingress/traefik/tls/traefik-controller.yaml b/manifests/ingress/traefik/tls/traefik-controller.yaml
new file mode 100644
index 0000000..26fbb23
--- /dev/null
+++ b/manifests/ingress/traefik/tls/traefik-controller.yaml
@@ -0,0 +1,160 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: traefik-conf
+ namespace: kube-system
+data:
+ traefik.toml: |
+ # 设置insecureSkipVerify = true,可以配置backend为443(比如dashboard)的ingress规则
+ insecureSkipVerify = true
+ defaultEntryPoints = ["http", "https"]
+ [entryPoints]
+ [entryPoints.http]
+ address = ":80"
+ ### 配置http 强制跳转 https
+ #[entryPoints.http.redirect]
+ # entryPoint = "https"
+ ### 配置只信任trustedIPs传递过来X-Forwarded-*,默认全部信任;为了防止客户端地址伪造,需开启这个
+ #[entryPoints.http.forwardedHeaders]
+ # trustedIPs = ["10.1.0.0/16", "172.20.0.0/16", "192.168.1.3"]
+ [entryPoints.https]
+ address = ":443"
+ [entryPoints.https.tls]
+ [[entryPoints.https.tls.certificates]]
+ CertFile = "/ssl/tls.crt"
+ KeyFile = "/ssl/tls.key"
+---
+kind: Deployment
+apiVersion: apps/v1beta1
+metadata:
+ name: traefik-ingress-controller
+ namespace: kube-system
+ labels:
+ k8s-app: traefik-ingress-lb
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: traefik-ingress-lb
+ template:
+ metadata:
+ labels:
+ k8s-app: traefik-ingress-lb
+ name: traefik-ingress-lb
+ spec:
+ serviceAccountName: traefik-ingress-controller
+ terminationGracePeriodSeconds: 60
+ volumes:
+ - name: ssl
+ secret:
+ secretName: traefik-cert
+ - name: config
+ configMap:
+ name: traefik-conf
+ #nodeSelector:
+ # node-role.kubernetes.io/traefik: "true"
+ containers:
+ - image: traefik:v1.7.4
+ imagePullPolicy: IfNotPresent
+ name: traefik-ingress-lb
+ volumeMounts:
+ - mountPath: "/ssl"
+ name: "ssl"
+ - mountPath: "/config"
+ name: "config"
+ resources:
+ limits:
+ cpu: 1000m
+ memory: 800Mi
+ requests:
+ cpu: 500m
+ memory: 600Mi
+ args:
+ - --configfile=/config/traefik.toml
+ - --api
+ - --kubernetes
+ - --logLevel=INFO
+ securityContext:
+ capabilities:
+ drop:
+ - ALL
+ add:
+ - NET_BIND_SERVICE
+ ports:
+ - name: http
+ containerPort: 80
+ hostPort: 80
+ - name: https
+ containerPort: 443
+ hostPort: 443
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: traefik-ingress-service
+ namespace: kube-system
+spec:
+ selector:
+ k8s-app: traefik-ingress-lb
+ ports:
+ - protocol: TCP
+ # 该端口为 traefik ingress-controller的服务端口
+ port: 80
+ # 集群hosts文件中设置的 NODE_PORT_RANGE 作为 NodePort的可用范围
+ # 从默认20000~40000之间选一个可用端口,让ingress-controller暴露给外部的访问
+ nodePort: 23456
+ name: http
+ - protocol: TCP
+ #
+ port: 443
+ nodePort: 23457
+ name: https
+ - protocol: TCP
+ # 该端口为 traefik 的管理WEB界面
+ port: 8080
+ name: admin
+ type: NodePort
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: traefik-ingress-controller
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - pods
+ - services
+ - endpoints
+ - secrets
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - extensions
+ resources:
+ - ingresses
+ verbs:
+ - get
+ - list
+ - watch
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: traefik-ingress-controller
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: traefik-ingress-controller
+subjects:
+- kind: ServiceAccount
+ name: traefik-ingress-controller
+ namespace: kube-system
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: traefik-ingress-controller
+ namespace: kube-system
diff --git a/manifests/ingress/traefik/traefik-ingress.yaml b/manifests/ingress/traefik/traefik-ingress.yaml
new file mode 100644
index 0000000..758f141
--- /dev/null
+++ b/manifests/ingress/traefik/traefik-ingress.yaml
@@ -0,0 +1,95 @@
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: traefik-ingress-controller
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - pods
+ - services
+ - endpoints
+ - secrets
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - extensions
+ resources:
+ - ingresses
+ verbs:
+ - get
+ - list
+ - watch
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: traefik-ingress-controller
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: traefik-ingress-controller
+subjects:
+- kind: ServiceAccount
+ name: traefik-ingress-controller
+ namespace: kube-system
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: traefik-ingress-controller
+ namespace: kube-system
+---
+kind: Deployment
+apiVersion: apps/v1beta1
+metadata:
+ name: traefik-ingress-controller
+ namespace: kube-system
+ labels:
+ k8s-app: traefik-ingress-lb
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ k8s-app: traefik-ingress-lb
+ template:
+ metadata:
+ labels:
+ k8s-app: traefik-ingress-lb
+ name: traefik-ingress-lb
+ spec:
+ serviceAccountName: traefik-ingress-controller
+ terminationGracePeriodSeconds: 60
+ containers:
+ - image: traefik:v1.7.4
+ imagePullPolicy: IfNotPresent
+ name: traefik-ingress-lb
+ args:
+ - --api
+ - --kubernetes
+ - --logLevel=INFO
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: traefik-ingress-service
+ namespace: kube-system
+spec:
+ selector:
+ k8s-app: traefik-ingress-lb
+ ports:
+ - protocol: TCP
+ # 该端口为 traefik ingress-controller的服务端口
+ port: 80
+ # 集群hosts文件中设置的 NODE_PORT_RANGE 作为 NodePort的可用范围
+ # 从默认20000~40000之间选一个可用端口,让ingress-controller暴露给外部的访问
+ nodePort: 23456
+ name: web
+ - protocol: TCP
+ # 该端口为 traefik 的管理WEB界面
+ port: 8080
+ name: admin
+ type: NodePort
diff --git a/manifests/ingress/traefik/traefik-ui.ing.yaml b/manifests/ingress/traefik/traefik-ui.ing.yaml
new file mode 100644
index 0000000..0787078
--- /dev/null
+++ b/manifests/ingress/traefik/traefik-ui.ing.yaml
@@ -0,0 +1,15 @@
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: traefik-web-ui
+ namespace: kube-system
+spec:
+ rules:
+ - host: traefik-ui.test.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: traefik-ingress-service
+ servicePort: 8080
diff --git a/manifests/ingress/whoami.ing.yaml b/manifests/ingress/whoami.ing.yaml
new file mode 100644
index 0000000..d1ab560
--- /dev/null
+++ b/manifests/ingress/whoami.ing.yaml
@@ -0,0 +1,15 @@
+# kubectl run whoami --image=emilevauge/whoami --port=80 --expose
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: test-whoami
+spec:
+ rules:
+ - host: who.test.com
+ http:
+ paths:
+ - path: /
+ backend:
+ serviceName: whoami
+ servicePort: 80
+
diff --git a/manifests/jenkins/.helmignore b/manifests/jenkins/.helmignore
new file mode 100644
index 0000000..f0c1319
--- /dev/null
+++ b/manifests/jenkins/.helmignore
@@ -0,0 +1,21 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
diff --git a/manifests/jenkins/Chart.yaml b/manifests/jenkins/Chart.yaml
new file mode 100644
index 0000000..c6033f3
--- /dev/null
+++ b/manifests/jenkins/Chart.yaml
@@ -0,0 +1,18 @@
+name: jenkins
+home: https://jenkins.io/
+version: 0.16.6
+appVersion: 2.121.1
+description: Open source continuous integration server. It supports multiple SCM tools
+ including CVS, Subversion and Git. It can execute Apache Ant and Apache Maven-based
+ projects as well as arbitrary scripts.
+sources:
+- https://github.com/jenkinsci/jenkins
+- https://github.com/jenkinsci/docker-jnlp-slave
+maintainers:
+- name: lachie83
+ email: lachlan.evenson@microsoft.com
+- name: viglesiasce
+ email: viglesias@google.com
+- name: lusyoe
+ email: lusyoe@163.com
+icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png
diff --git a/manifests/jenkins/OWNERS b/manifests/jenkins/OWNERS
new file mode 100644
index 0000000..f5544ea
--- /dev/null
+++ b/manifests/jenkins/OWNERS
@@ -0,0 +1,6 @@
+approvers:
+- lachie83
+- viglesiasce
+reviewers:
+- lachie83
+- viglesiasce
diff --git a/manifests/jenkins/README.md b/manifests/jenkins/README.md
new file mode 100644
index 0000000..8757c39
--- /dev/null
+++ b/manifests/jenkins/README.md
@@ -0,0 +1,236 @@
+# Jenkins Helm Chart
+
+Jenkins master and slave cluster utilizing the Jenkins Kubernetes plugin
+
+* https://wiki.jenkins-ci.org/display/JENKINS/Kubernetes+Plugin
+
+Inspired by the awesome work of Carlos Sanchez
+
+## Chart Details
+
+This chart will do the following:
+
+* 1 x Jenkins Master with port 8080 exposed on an external LoadBalancer
+* All using Kubernetes Deployments
+
+## Installing the Chart
+
+To install the chart with the release name `my-release`:
+
+```bash
+$ helm install --name my-release stable/jenkins
+```
+
+## Configuration
+
+The following tables list the configurable parameters of the Jenkins chart and their default values.
+
+### Jenkins Master
+| Parameter | Description | Default |
+| --------------------------------- | ------------------------------------ | ---------------------------------------------------------------------------- |
+| `nameOverride` | Override the resource name prefix | `jenkins` |
+| `fullnameOverride` | Override the full resource names | `jenkins-{release-name}` (or `jenkins` if release-name is `jenkins`) |
+| `Master.Name` | Jenkins master name | `jenkins-master` |
+| `Master.Image` | Master image name | `jenkinsci/jenkins` |
+| `Master.ImageTag` | Master image tag | `lts` |
+| `Master.ImagePullPolicy` | Master image pull policy | `Always` |
+| `Master.ImagePullSecret` | Master image pull secret | Not set |
+| `Master.Component` | k8s selector key | `jenkins-master` |
+| `Master.UseSecurity` | Use basic security | `true` |
+| `Master.AdminUser` | Admin username (and password) created as a secret if useSecurity is true | `admin` |
+| `Master.resources` | Resources allocation (Requests and Limits) | `{requests: {cpu: 50m, memory: 256Mi}, limits: {cpu: 2000m, memory: 2048Mi}}`|
+| `Master.InitContainerEnv` | Environment variables for Init Container | Not set |
+| `Master.ContainerEnv` | Environment variables for Jenkins Container | Not set |
+| `Master.UsePodSecurityContext` | Enable pod security context (must be `true` if `RunAsUser` or `FsGroup` are set) | `true` |
+| `Master.RunAsUser` | uid that jenkins runs with | `0` |
+| `Master.FsGroup` | uid that will be used for persistent volume | `0` |
+| `Master.ServiceAnnotations` | Service annotations | `{}` |
+| `Master.ServiceType` | k8s service type | `LoadBalancer` |
+| `Master.ServicePort` | k8s service port | `8080` |
+| `Master.NodePort` | k8s node port | Not set |
+| `Master.HealthProbes` | Enable k8s liveness and readiness probes | `true` |
+| `Master.HealthProbesLivenessTimeout` | Set the timeout for the liveness probe | `120` |
+| `Master.HealthProbesReadinessTimeout` | Set the timeout for the readiness probe | `60` |
+| `Master.HealthProbeLivenessFailureThreshold` | Set the failure threshold for the liveness probe | `12` |
+| `Master.ContainerPort` | Master listening port | `8080` |
+| `Master.SlaveListenerPort` | Listening port for agents | `50000` |
+| `Master.DisabledAgentProtocols` | Disabled agent protocols | `JNLP-connect JNLP2-connect` |
+| `Master.CSRF.DefaultCrumbIssuer.Enabled` | Enable the default CSRF Crumb issuer | `true` |
+| `Master.CSRF.DefaultCrumbIssuer.ProxyCompatability` | Enable proxy compatibility | `true` |
+| `Master.CLI` | Enable CLI over remoting | `false` |
+| `Master.LoadBalancerSourceRanges` | Allowed inbound IP addresses | `0.0.0.0/0` |
+| `Master.LoadBalancerIP` | Optional fixed external IP | Not set |
+| `Master.JMXPort` | Open a port, for JMX stats | Not set |
+| `Master.CustomConfigMap` | Use a custom ConfigMap | `false` |
+| `Master.Ingress.Annotations` | Ingress annotations | `{}` |
+| `Master.Ingress.TLS` | Ingress TLS configuration | `[]` |
+| `Master.InitScripts` | List of Jenkins init scripts | Not set |
+| `Master.CredentialsXmlSecret` | Kubernetes secret that contains a 'credentials.xml' file | Not set |
+| `Master.SecretsFilesSecret` | Kubernetes secret that contains 'secrets' files | Not set |
+| `Master.Jobs` | Jenkins XML job configs | Not set |
+| `Master.InstallPlugins` | List of Jenkins plugins to install | `kubernetes:0.11 workflow-aggregator:2.5 credentials-binding:1.11 git:3.2.0` |
+| `Master.ScriptApproval` | List of groovy functions to approve | Not set |
+| `Master.NodeSelector` | Node labels for pod assignment | `{}` |
+| `Master.Affinity` | Affinity settings | `{}` |
+| `Master.Tolerations` | Toleration labels for pod assignment | `{}` |
+| `Master.PodAnnotations` | Annotations for master pod | `{}` |
+| `NetworkPolicy.Enabled` | Enable creation of NetworkPolicy resources. | `false` |
+| `NetworkPolicy.ApiVersion` | NetworkPolicy ApiVersion | `extensions/v1beta1` |
+| `rbac.install` | Create service account and ClusterRoleBinding for Kubernetes plugin | `false` |
+| `rbac.apiVersion` | RBAC API version | `v1beta1` |
+| `rbac.roleRef` | Cluster role name to bind to | `cluster-admin` |
+
+### Jenkins Agent
+
+| Parameter | Description | Default |
+| ----------------------- | ----------------------------------------------- | ---------------------- |
+| `Agent.AlwaysPullImage` | Always pull agent container image before build | `false` |
+| `Agent.Enabled` | Enable Kubernetes plugin jnlp-agent podTemplate | `true` |
+| `Agent.Image` | Agent image name | `jenkinsci/jnlp-slave` |
+| `Agent.ImagePullSecret` | Agent image pull secret | Not set |
+| `Agent.ImageTag` | Agent image tag | `2.62` |
+| `Agent.Privileged` | Agent privileged container | `false` |
+| `Agent.resources` | Resources allocation (Requests and Limits) | `{requests: {cpu: 200m, memory: 256Mi}, limits: {cpu: 200m, memory: 256Mi}}`|
+| `Agent.volumes` | Additional volumes | `nil` |
+
+Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`.
+
+Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example,
+
+```bash
+$ helm install --name my-release -f values.yaml stable/jenkins
+```
+
+> **Tip**: You can use the default [values.yaml](values.yaml)
+
+## Mounting volumes into your Agent pods
+
+Your Jenkins Agents will run as pods, and it's possible to inject volumes where needed:
+
+```yaml
+Agent:
+ volumes:
+ - type: Secret
+ secretName: jenkins-mysecrets
+ mountPath: /var/run/secrets/jenkins-mysecrets
+```
+
+The supported volume types are: `ConfigMap`, `EmptyDir`, `HostPath`, `Nfs`, `Pod`, `Secret`. Each type supports a different set of configurable attributes, defined by [the corresponding Java class](https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes).
+
+## NetworkPolicy
+
+To make use of the NetworkPolicy resources created by default,
+install [a networking plugin that implements the Kubernetes
+NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin).
+
+For Kubernetes v1.5 & v1.6, you must also turn on NetworkPolicy by setting
+the DefaultDeny namespace annotation. Note: this will enforce policy for _all_ pods in the namespace:
+
+ kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}"
+
+Install helm chart with network policy enabled:
+
+ $ helm install stable/jenkins --set NetworkPolicy.Enabled=true
+
+## Persistence
+
+The Jenkins image stores persistence under `/var/jenkins_home` path of the container. A dynamically managed Persistent Volume
+Claim is used to keep the data across deployments, by default. This is known to work in GCE, AWS, and minikube. Alternatively,
+a previously configured Persistent Volume Claim can be used.
+
+It is possible to mount several volumes using `Persistence.volumes` and `Persistence.mounts` parameters.
+
+### Persistence Values
+
+| Parameter | Description | Default |
+| --------------------------- | ------------------------------- | --------------- |
+| `Persistence.Enabled` | Enable the use of a Jenkins PVC | `true` |
+| `Persistence.ExistingClaim` | Provide the name of a PVC | `nil` |
+| `Persistence.AccessMode` | The PVC access mode | `ReadWriteOnce` |
+| `Persistence.Size` | The size of the PVC | `8Gi` |
+| `Persistence.volumes` | Additional volumes | `nil` |
+| `Persistence.mounts` | Additional mounts | `nil` |
+| `Persistence.StorageClass` | The PV Provisioner | `nfs-dynamic-class`|
+
+#### Existing PersistentVolumeClaim
+
+1. Create the PersistentVolume
+1. Create the PersistentVolumeClaim
+1. Install the chart
+
+```bash
+$ helm install --name my-release --set Persistence.ExistingClaim=PVC_NAME stable/jenkins
+```
+
+## Custom ConfigMap
+
+When creating a new parent chart with this chart as a dependency, the `CustomConfigMap` parameter can be used to override the default config.xml provided.
+It also allows for providing additional xml configuration files that will be copied into `/var/jenkins_home`. In the parent chart's values.yaml,
+set the `jenkins.Master.CustomConfigMap` value to true like so
+
+```yaml
+jenkins:
+ Master:
+ CustomConfigMap: true
+```
+
+and provide the file `templates/config.tpl` in your parent chart for your use case. You can start by copying the contents of `config.yaml` from this chart into your parent charts `templates/config.tpl` as a basis for customization. Finally, you'll need to wrap the contents of `templates/config.tpl` like so:
+
+```yaml
+{{- define "override_config_map" }}
+
+{{ end }}
+```
+
+## RBAC
+
+If running upon a cluster with RBAC enabled you will need to do the following:
+
+* `helm install stable/jenkins --set rbac.install=true`
+* Create a Jenkins credential of type Kubernetes service account with service account name provided in the `helm status` output.
+* Under configure Jenkins -- Update the credentials config in the cloud section to use the service account credential you created in the step above.
+
+## Run Jenkins as non root user
+
+The default settings of this helm chart let Jenkins run as root user with uid `0`.
+Due to security reasons you may want to run Jenkins as a non root user.
+Fortunately the default jenkins docker image `jenkins/jenkins` contains a user `jenkins` with uid `1000` that can be used for this purpose.
+
+Simply use the following settings to run Jenkins as `jenkins` user with uid `1000`.
+
+```yaml
+jenkins:
+ Master:
+ RunAsUser: 1000
+ FsGroup: 1000
+```
+
+Docs taken from https://github.com/jenkinsci/docker/blob/master/Dockerfile:
+_Jenkins is run with user `jenkins`, uid = 1000. If you bind mount a volume from the host or a data container,ensure you use the same uid_
+
+## Running behind a forward proxy
+
+The master pod uses an Init Container to install plugins etc. If you are behind a corporate proxy it may be useful to set `Master.InitContainerEnv` to add environment variables such as `http_proxy`, so that these can be downloaded.
+
+Additionally, you may want to add env vars for the Jenkins container, and the JVM (`Master.JavaOpts`).
+
+```yaml
+Master:
+ InitContainerEnv:
+ - name: http_proxy
+ value: "http://192.168.64.1:3128"
+ - name: https_proxy
+ value: "http://192.168.64.1:3128"
+ - name: no_proxy
+ value: ""
+ ContainerEnv:
+ - name: http_proxy
+ value: "http://192.168.64.1:3128"
+ - name: https_proxy
+ value: "http://192.168.64.1:3128"
+ JavaOpts: >-
+ -Dhttp.proxyHost=192.168.64.1
+ -Dhttp.proxyPort=3128
+ -Dhttps.proxyHost=192.168.64.1
+ -Dhttps.proxyPort=3128
+```
diff --git a/manifests/jenkins/templates/NOTES.txt b/manifests/jenkins/templates/NOTES.txt
new file mode 100644
index 0000000..dc9e5e4
--- /dev/null
+++ b/manifests/jenkins/templates/NOTES.txt
@@ -0,0 +1,45 @@
+1. Get your '{{ .Values.Master.AdminUser }}' user password by running:
+ printf $(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "jenkins.fullname" . }} -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
+
+{{- if .Values.Master.HostName }}
+
+2. Visit http://{{ .Values.Master.HostName }}
+{{- else }}
+2. Get the Jenkins URL to visit by running these commands in the same shell:
+{{- if contains "NodePort" .Values.Master.ServiceType }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "jenkins.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT/login
+
+{{- else if contains "LoadBalancer" .Values.Master.ServiceType }}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "jenkins.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "jenkins.fullname" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}")
+ echo http://$SERVICE_IP:{{ .Values.Master.ServicePort }}/login
+
+{{- else if contains "ClusterIP" .Values.Master.ServiceType }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "component={{ template "jenkins.fullname" . }}-master" -o jsonpath="{.items[0].metadata.name}")
+ echo http://127.0.0.1:{{ .Values.Master.ServicePort }}
+ kubectl port-forward $POD_NAME {{ .Values.Master.ServicePort }}:{{ .Values.Master.ServicePort }}
+
+{{- end }}
+{{- end }}
+
+3. Login with the password from step 1 and the username: {{ .Values.Master.AdminUser }}
+
+For more information on running Jenkins on Kubernetes, visit:
+https://cloud.google.com/solutions/jenkins-on-container-engine
+
+{{- if .Values.Persistence.Enabled }}
+{{- else }}
+#################################################################################
+###### WARNING: Persistence is disabled!!! You will lose your data when #####
+###### the Jenkins pod is terminated. #####
+#################################################################################
+{{- end }}
+
+{{- if .Values.rbac.install }}
+Configure the Kubernetes plugin in Jenkins to use the following Service Account name {{ template "jenkins.fullname" . }} using the following steps:
+ Create a Jenkins credential of type Kubernetes service account with service account name {{ template "jenkins.fullname" . }}
+ Under configure Jenkins -- Update the credentials config in the cloud section to use the service account credential you created in the step above.
+{{- end }}
diff --git a/manifests/jenkins/templates/_helpers.tpl b/manifests/jenkins/templates/_helpers.tpl
new file mode 100644
index 0000000..eac695f
--- /dev/null
+++ b/manifests/jenkins/templates/_helpers.tpl
@@ -0,0 +1,34 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "jenkins.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "jenkins.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "jenkins.kubernetes-version" -}}
+ {{- range .Values.Master.InstallPlugins -}}
+ {{ if hasPrefix "kubernetes:" . }}
+ {{- $split := splitList ":" . }}
+ {{- printf "%s" (index $split 1 ) -}}
+ {{- end -}}
+ {{- end -}}
+{{- end -}}
diff --git a/manifests/jenkins/templates/config.yaml b/manifests/jenkins/templates/config.yaml
new file mode 100644
index 0000000..bdcf238
--- /dev/null
+++ b/manifests/jenkins/templates/config.yaml
@@ -0,0 +1,218 @@
+{{- if not .Values.Master.CustomConfigMap }}
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "jenkins.fullname" . }}
+data:
+ config.xml: |-
+
+
+
+ {{ .Values.Master.ImageTag }}
+ 0
+ NORMAL
+ {{ .Values.Master.UseSecurity }}
+
+ true
+
+
+ false
+
+ ${JENKINS_HOME}/workspace/${ITEM_FULLNAME}
+ ${ITEM_ROOTDIR}/builds
+
+
+
+
+
+
+ kubernetes
+
+{{- if .Values.Agent.Enabled }}
+
+
+ default
+ 2147483647
+ 0
+ {{ .Values.Agent.Component }}
+
+ {{- $local := dict "first" true }}
+ {{- range $key, $value := .Values.Agent.NodeSelector }}
+ {{- if not $local.first }},{{- end }}
+ {{- $key }}={{ $value }}
+ {{- $_ := set $local "first" false }}
+ {{- end }}
+ NORMAL
+
+{{- range $index, $volume := .Values.Agent.volumes }}
+
+{{- range $key, $value := $volume }}{{- if not (eq $key "type") }}
+ <{{ $key }}>{{ $value }}{{ $key }}>
+{{- end }}{{- end }}
+
+{{- end }}
+
+
+
+ jnlp
+ {{ .Values.Agent.Image }}:{{ .Values.Agent.ImageTag }}
+{{- if .Values.Agent.Privileged }}
+ true
+{{- else }}
+ false
+{{- end }}
+ {{ .Values.Agent.AlwaysPullImage }}
+ /home/jenkins
+
+ ${computer.jnlpmac} ${computer.name}
+ false
+ # Resources configuration is a little hacky. This was to prevent breaking
+ # changes, and should be cleanned up in the future once everybody had
+ # enough time to migrate.
+ {{.Values.Agent.Cpu | default .Values.Agent.resources.requests.cpu}}
+ {{.Values.Agent.Memory | default .Values.Agent.resources.requests.memory}}
+ {{.Values.Agent.Cpu | default .Values.Agent.resources.limits.cpu}}
+ {{.Values.Agent.Memory | default .Values.Agent.resources.limits.memory}}
+
+
+
+
+{{- if .Values.Agent.ImagePullSecret }}
+
+
+ {{ .Values.Agent.ImagePullSecret }}
+
+
+{{- else }}
+
+{{- end }}
+
+
+{{- end -}}
+
+ https://kubernetes
+ false
+ {{ .Release.Namespace }}
+ http://{{ template "jenkins.fullname" . }}:{{.Values.Master.ServicePort}}{{ default "" .Values.Master.JenkinsUriPrefix }}
+ {{ template "jenkins.fullname" . }}-agent:50000
+ 10
+ 5
+ 0
+ 0
+
+
+ 5
+ 0
+
+
+
+ All
+ false
+ false
+
+
+
+ All
+ 50000
+
+{{- range .Values.Master.DisabledAgentProtocols }}
+ {{ . }}
+{{- end }}
+
+
+{{- if .Values.Master.CSRF.DefaultCrumbIssuer.Enabled }}
+
+{{- if .Values.Master.CSRF.DefaultCrumbIssuer.ProxyCompatability }}
+ true
+{{- end }}
+
+{{- end }}
+
+
+ true
+
+{{- if .Values.Master.ScriptApproval }}
+ scriptapproval.xml: |-
+
+
+
+
+{{- range $key, $val := .Values.Master.ScriptApproval }}
+ {{ $val }}
+{{- end }}
+
+
+
+
+
+
+
+{{- end }}
+ jenkins.CLI.xml: |-
+
+
+{{- if .Values.Master.CLI }}
+ true
+{{- else }}
+ false
+{{- end }}
+
+ hudson.model.UpdateCenter.xml: |-
+
+
+
+ default
+{{- if .Values.Master.UpdateCenter }}
+ {{ .Values.Master.UpdateCenter }}
+{{- else }}
+ https://updates.jenkins.io/update-center.json
+{{- end }}
+
+
+ apply_config.sh: |-
+ mkdir -p /usr/share/jenkins/ref/secrets/;
+ echo "false" > /usr/share/jenkins/ref/secrets/slave-to-master-security-kill-switch;
+ cp -n /var/jenkins_config/config.xml /var/jenkins_home;
+ cp -n /var/jenkins_config/jenkins.CLI.xml /var/jenkins_home;
+ cp -n /var/jenkins_config/hudson.model.UpdateCenter.xml /var/jenkins_home;
+{{- if .Values.Master.InstallPlugins }}
+ # Install missing plugins
+ cp /var/jenkins_config/plugins.txt /var/jenkins_home;
+ rm -rf /usr/share/jenkins/ref/plugins/*.lock
+ /usr/local/bin/install-plugins.sh `echo $(cat /var/jenkins_home/plugins.txt)`;
+ # Copy plugins to shared volume
+ cp -n /usr/share/jenkins/ref/plugins/* /var/jenkins_plugins;
+{{- end }}
+{{- if .Values.Master.ScriptApproval }}
+ cp -n /var/jenkins_config/scriptapproval.xml /var/jenkins_home/scriptApproval.xml;
+{{- end }}
+{{- if .Values.Master.InitScripts }}
+ mkdir -p /var/jenkins_home/init.groovy.d/;
+ cp -n /var/jenkins_config/*.groovy /var/jenkins_home/init.groovy.d/
+{{- end }}
+{{- if .Values.Master.CredentialsXmlSecret }}
+ cp -n /var/jenkins_credentials/credentials.xml /var/jenkins_home;
+{{- end }}
+{{- if .Values.Master.SecretsFilesSecret }}
+ cp -n /var/jenkins_secrets/* /usr/share/jenkins/ref/secrets;
+{{- end }}
+{{- if .Values.Master.Jobs }}
+ for job in $(ls /var/jenkins_jobs); do
+ mkdir -p /var/jenkins_home/jobs/$job
+ cp -n /var/jenkins_jobs/$job /var/jenkins_home/jobs/$job/config.xml
+ done
+{{- end }}
+{{- range $key, $val := .Values.Master.InitScripts }}
+ init{{ $key }}.groovy: |-
+{{ $val | indent 4 }}
+{{- end }}
+ plugins.txt: |-
+{{- if .Values.Master.InstallPlugins }}
+{{- range $index, $val := .Values.Master.InstallPlugins }}
+{{ $val | indent 4 }}
+{{- end }}
+{{- end }}
+{{ else }}
+{{ include "override_config_map" . }}
+{{- end -}}
diff --git a/manifests/jenkins/templates/home-pvc.yaml b/manifests/jenkins/templates/home-pvc.yaml
new file mode 100644
index 0000000..d6e44f2
--- /dev/null
+++ b/manifests/jenkins/templates/home-pvc.yaml
@@ -0,0 +1,28 @@
+{{- if and .Values.Persistence.Enabled (not .Values.Persistence.ExistingClaim) -}}
+kind: PersistentVolumeClaim
+apiVersion: v1
+metadata:
+{{- if .Values.Persistence.Annotations }}
+ annotations:
+{{ toYaml .Values.Persistence.Annotations | indent 4 }}
+{{- end }}
+ name: {{ template "jenkins.fullname" . }}
+ labels:
+ app: {{ template "jenkins.fullname" . }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ release: "{{ .Release.Name }}"
+ heritage: "{{ .Release.Service }}"
+spec:
+ accessModes:
+ - {{ .Values.Persistence.AccessMode | quote }}
+ resources:
+ requests:
+ storage: {{ .Values.Persistence.Size | quote }}
+{{- if .Values.Persistence.StorageClass }}
+{{- if (eq "-" .Values.Persistence.StorageClass) }}
+ storageClassName: ""
+{{- else }}
+ storageClassName: "{{ .Values.Persistence.StorageClass }}"
+{{- end }}
+{{- end }}
+{{- end }}
diff --git a/manifests/jenkins/templates/jenkins-agent-svc.yaml b/manifests/jenkins/templates/jenkins-agent-svc.yaml
new file mode 100644
index 0000000..a9ad63d
--- /dev/null
+++ b/manifests/jenkins/templates/jenkins-agent-svc.yaml
@@ -0,0 +1,20 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ template "jenkins.fullname" . }}-agent
+ labels:
+ app: {{ template "jenkins.fullname" . }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ component: "{{ .Release.Name }}-{{ .Values.Master.Component }}"
+{{- if .Values.Master.SlaveListenerServiceAnnotations }}
+ annotations:
+{{ toYaml .Values.Master.SlaveListenerServiceAnnotations | indent 4 }}
+{{- end }}
+spec:
+ ports:
+ - port: {{ .Values.Master.SlaveListenerPort }}
+ targetPort: {{ .Values.Master.SlaveListenerPort }}
+ name: slavelistener
+ selector:
+ component: "{{ .Release.Name }}-{{ .Values.Master.Component }}"
+ type: {{ .Values.Master.SlaveListenerServiceType }}
diff --git a/manifests/jenkins/templates/jenkins-master-deployment.yaml b/manifests/jenkins/templates/jenkins-master-deployment.yaml
new file mode 100644
index 0000000..fcda373
--- /dev/null
+++ b/manifests/jenkins/templates/jenkins-master-deployment.yaml
@@ -0,0 +1,231 @@
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: {{ template "jenkins.fullname" . }}
+ labels:
+ heritage: {{ .Release.Service | quote }}
+ release: {{ .Release.Name | quote }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ component: "{{ .Release.Name }}-{{ .Values.Master.Name }}"
+spec:
+ replicas: 1
+ strategy:
+ type: RollingUpdate
+ selector:
+ matchLabels:
+ component: "{{ .Release.Name }}-{{ .Values.Master.Component }}"
+ template:
+ metadata:
+ labels:
+ app: {{ template "jenkins.fullname" . }}
+ heritage: {{ .Release.Service | quote }}
+ release: {{ .Release.Name | quote }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ component: "{{ .Release.Name }}-{{ .Values.Master.Component }}"
+ annotations:
+ checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }}
+ {{- if .Values.Master.PodAnnotations }}
+{{ toYaml .Values.Master.PodAnnotations | indent 8 }}
+ {{- end }}
+ spec:
+ {{- if .Values.Master.NodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.Master.NodeSelector | indent 8 }}
+ {{- end }}
+ {{- if .Values.Master.Tolerations }}
+ tolerations:
+{{ toYaml .Values.Master.Tolerations | indent 8 }}
+ {{- end }}
+ {{- if .Values.Master.Affinity }}
+ affinity:
+{{ toYaml .Values.Master.Affinity | indent 8 }}
+ {{- end }}
+{{- if .Values.Master.UsePodSecurityContext }}
+ securityContext:
+ runAsUser: {{ default 0 .Values.Master.RunAsUser }}
+{{- if and (.Values.Master.RunAsUser) (.Values.Master.FsGroup) }}
+{{- if not (eq .Values.Master.RunAsUser 0.0) }}
+ fsGroup: {{ .Values.Master.FsGroup }}
+{{- end }}
+{{- end }}
+{{- end }}
+ serviceAccountName: {{ if .Values.rbac.install }}{{ template "jenkins.fullname" . }}{{ else }}"{{ .Values.rbac.serviceAccountName }}"{{ end }}
+ initContainers:
+ - name: "copy-default-config"
+ image: "{{ .Values.Master.Image }}:{{ .Values.Master.ImageTag }}"
+ imagePullPolicy: "{{ .Values.Master.ImagePullPolicy }}"
+ command: [ "sh", "/var/jenkins_config/apply_config.sh" ]
+ {{- if .Values.Master.InitContainerEnv }}
+ env:
+{{ toYaml .Values.Master.InitContainerEnv | indent 12 }}
+ {{- end }}
+ volumeMounts:
+ -
+ mountPath: /var/jenkins_home
+ name: jenkins-home
+ -
+ mountPath: /var/jenkins_config
+ name: jenkins-config
+ {{- if .Values.Master.CredentialsXmlSecret }}
+ -
+ mountPath: /var/jenkins_credentials
+ name: jenkins-credentials
+ readOnly: true
+ {{- end }}
+ {{- if .Values.Master.SecretsFilesSecret }}
+ -
+ mountPath: /var/jenkins_secrets
+ name: jenkins-secrets
+ readOnly: true
+ {{- end }}
+ {{- if .Values.Master.Jobs }}
+ -
+ mountPath: /var/jenkins_jobs
+ name: jenkins-jobs
+ readOnly: true
+ {{- end }}
+ {{- if .Values.Master.InstallPlugins }}
+ -
+ mountPath: /var/jenkins_plugins
+ name: plugin-dir
+ {{- end }}
+ -
+ mountPath: /usr/share/jenkins/ref/secrets/
+ name: secrets-dir
+ containers:
+ - name: {{ template "jenkins.fullname" . }}
+ image: "{{ .Values.Master.Image }}:{{ .Values.Master.ImageTag }}"
+ imagePullPolicy: "{{ .Values.Master.ImagePullPolicy }}"
+ {{- if .Values.Master.UseSecurity }}
+ args: [ "--argumentsRealm.passwd.$(ADMIN_USER)=$(ADMIN_PASSWORD)", "--argumentsRealm.roles.$(ADMIN_USER)=admin"]
+ {{- end }}
+ env:
+ - name: JAVA_OPTS
+ value: "{{ default "" .Values.Master.JavaOpts}}"
+ - name: JENKINS_OPTS
+ value: "{{ if .Values.Master.JenkinsUriPrefix }}--prefix={{ .Values.Master.JenkinsUriPrefix }} {{ end }}{{ default "" .Values.Master.JenkinsOpts}}"
+ {{- if .Values.Master.UseSecurity }}
+ - name: ADMIN_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "jenkins.fullname" . }}
+ key: jenkins-admin-password
+ - name: ADMIN_USER
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "jenkins.fullname" . }}
+ key: jenkins-admin-user
+ {{- end }}
+ {{- if .Values.Master.ContainerEnv }}
+{{ toYaml .Values.Master.ContainerEnv | indent 12 }}
+ {{- end }}
+ ports:
+ - containerPort: {{ .Values.Master.ContainerPort }}
+ name: http
+ - containerPort: {{ .Values.Master.SlaveListenerPort }}
+ name: slavelistener
+ {{- if .Values.Master.JMXPort }}
+ - containerPort: {{ .Values.Master.JMXPort }}
+ name: jmx
+ {{- end }}
+{{- if .Values.Master.HealthProbes }}
+ livenessProbe:
+ httpGet:
+ path: /login
+ port: http
+ initialDelaySeconds: {{ .Values.Master.HealthProbesLivenessTimeout }}
+ timeoutSeconds: 5
+ failureThreshold: {{ .Values.Master.HealthProbeLivenessFailureThreshold }}
+ readinessProbe:
+ httpGet:
+ path: /login
+ port: http
+ initialDelaySeconds: {{ .Values.Master.HealthProbesReadinessTimeout }}
+{{- end }}
+ resources:
+{{ if or .Values.Master.Cpu .Values.Master.Memory }}
+ requests:
+ cpu: "{{ .Values.Master.Cpu }}"
+ memory: "{{ .Values.Master.Memory }}"
+{{ else }}
+{{ toYaml .Values.Master.resources | indent 12 }}
+{{ end }}
+ volumeMounts:
+{{- if .Values.Persistence.mounts }}
+{{ toYaml .Values.Persistence.mounts | indent 12 }}
+{{- end }}
+ -
+ mountPath: /var/jenkins_home
+ name: jenkins-home
+ readOnly: false
+ -
+ mountPath: /var/jenkins_config
+ name: jenkins-config
+ readOnly: true
+ {{- if .Values.Master.CredentialsXmlSecret }}
+ -
+ mountPath: /var/jenkins_credentials
+ name: jenkins-credentials
+ readOnly: true
+ {{- end }}
+ {{- if .Values.Master.SecretsFilesSecret }}
+ -
+ mountPath: /var/jenkins_secrets
+ name: jenkins-secrets
+ readOnly: true
+ {{- end }}
+ {{- if .Values.Master.Jobs }}
+ -
+ mountPath: /var/jenkins_jobs
+ name: jenkins-jobs
+ readOnly: true
+ {{- end }}
+ {{- if .Values.Master.InstallPlugins }}
+ -
+ mountPath: /usr/share/jenkins/ref/plugins/
+ name: plugin-dir
+ readOnly: false
+ {{- end }}
+ -
+ mountPath: /usr/share/jenkins/ref/secrets/
+ name: secrets-dir
+ readOnly: false
+ volumes:
+{{- if .Values.Persistence.volumes }}
+{{ toYaml .Values.Persistence.volumes | indent 6 }}
+{{- end }}
+ - name: jenkins-config
+ configMap:
+ name: {{ template "jenkins.fullname" . }}
+ {{- if .Values.Master.CredentialsXmlSecret }}
+ - name: jenkins-credentials
+ secret:
+ secretName: {{ .Values.Master.CredentialsXmlSecret }}
+ {{- end }}
+ {{- if .Values.Master.SecretsFilesSecret }}
+ - name: jenkins-secrets
+ secret:
+ secretName: {{ .Values.Master.SecretsFilesSecret }}
+ {{- end }}
+ {{- if .Values.Master.Jobs }}
+ - name: jenkins-jobs
+ configMap:
+ name: {{ template "jenkins.fullname" . }}-jobs
+ {{- end }}
+ {{- if .Values.Master.InstallPlugins }}
+ - name: plugin-dir
+ emptyDir: {}
+ {{- end }}
+ - name: secrets-dir
+ emptyDir: {}
+ - name: jenkins-home
+ {{- if .Values.Persistence.Enabled }}
+ persistentVolumeClaim:
+ claimName: {{ .Values.Persistence.ExistingClaim | default (include "jenkins.fullname" .) }}
+ {{- else }}
+ emptyDir: {}
+ {{- end -}}
+{{- if .Values.Master.ImagePullSecret }}
+ imagePullSecrets:
+ - name: {{ .Values.Master.ImagePullSecret }}
+{{- end -}}
diff --git a/manifests/jenkins/templates/jenkins-master-ingress.yaml b/manifests/jenkins/templates/jenkins-master-ingress.yaml
new file mode 100644
index 0000000..68fd535
--- /dev/null
+++ b/manifests/jenkins/templates/jenkins-master-ingress.yaml
@@ -0,0 +1,22 @@
+{{- if .Values.Master.HostName }}
+apiVersion: {{ .Values.Master.Ingress.ApiVersion }}
+kind: Ingress
+metadata:
+{{- if .Values.Master.Ingress.Annotations }}
+ annotations:
+{{ toYaml .Values.Master.Ingress.Annotations | indent 4 }}
+{{- end }}
+ name: {{ template "jenkins.fullname" . }}
+spec:
+ rules:
+ - host: {{ .Values.Master.HostName | quote }}
+ http:
+ paths:
+ - backend:
+ serviceName: {{ template "jenkins.fullname" . }}
+ servicePort: {{ .Values.Master.ServicePort }}
+{{- if .Values.Master.Ingress.TLS }}
+ tls:
+{{ toYaml .Values.Master.Ingress.TLS | indent 4 }}
+{{- end -}}
+{{- end }}
diff --git a/manifests/jenkins/templates/jenkins-master-networkpolicy.yaml b/manifests/jenkins/templates/jenkins-master-networkpolicy.yaml
new file mode 100644
index 0000000..6034c91
--- /dev/null
+++ b/manifests/jenkins/templates/jenkins-master-networkpolicy.yaml
@@ -0,0 +1,33 @@
+{{- if .Values.NetworkPolicy.Enabled }}
+kind: NetworkPolicy
+apiVersion: {{ .Values.NetworkPolicy.ApiVersion }}
+metadata:
+ name: "{{ .Release.Name }}-{{ .Values.Master.Component }}"
+spec:
+ podSelector:
+ matchLabels:
+ component: "{{ .Release.Name }}-{{ .Values.Master.Component }}"
+ ingress:
+ # Allow web access to the UI
+ - ports:
+ - port: {{ .Values.Master.ContainerPort }}
+ # Allow inbound connections from slave
+ - from:
+ - podSelector:
+ matchLabels:
+ "jenkins/{{ .Release.Name }}-{{ .Values.Agent.Component }}": "true"
+ ports:
+ - port: {{ .Values.Master.SlaveListenerPort }}
+{{- if .Values.Agent.Enabled }}
+---
+kind: NetworkPolicy
+apiVersion: {{ .Values.NetworkPolicy.ApiVersion }}
+metadata:
+ name: "{{ .Release.Name }}-{{ .Values.Agent.Component }}"
+spec:
+ podSelector:
+ matchLabels:
+ # DefaultDeny
+ "jenkins/{{ .Release.Name }}-{{ .Values.Agent.Component }}": "true"
+{{- end }}
+{{- end }}
diff --git a/manifests/jenkins/templates/jenkins-master-svc.yaml b/manifests/jenkins/templates/jenkins-master-svc.yaml
new file mode 100644
index 0000000..b028744
--- /dev/null
+++ b/manifests/jenkins/templates/jenkins-master-svc.yaml
@@ -0,0 +1,31 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{template "jenkins.fullname" . }}
+ labels:
+ app: {{ template "jenkins.fullname" . }}
+ heritage: {{.Release.Service | quote }}
+ release: {{.Release.Name | quote }}
+ chart: "{{.Chart.Name}}-{{.Chart.Version}}"
+ component: "{{.Release.Name}}-{{.Values.Master.Component}}"
+{{- if .Values.Master.ServiceAnnotations }}
+ annotations:
+{{ toYaml .Values.Master.ServiceAnnotations | indent 4 }}
+{{- end }}
+spec:
+ ports:
+ - port: {{.Values.Master.ServicePort}}
+ name: http
+ targetPort: {{.Values.Master.ContainerPort}}
+ {{if (and (eq .Values.Master.ServiceType "NodePort") (not (empty .Values.Master.NodePort)))}}
+ nodePort: {{.Values.Master.NodePort}}
+ {{end}}
+ selector:
+ component: "{{.Release.Name}}-{{.Values.Master.Component}}"
+ type: {{.Values.Master.ServiceType}}
+ {{if eq .Values.Master.ServiceType "LoadBalancer"}}
+ loadBalancerSourceRanges: {{.Values.Master.LoadBalancerSourceRanges}}
+ {{if .Values.Master.LoadBalancerIP}}
+ loadBalancerIP: {{.Values.Master.LoadBalancerIP}}
+ {{end}}
+ {{end}}
diff --git a/manifests/jenkins/templates/jenkins-test.yaml b/manifests/jenkins/templates/jenkins-test.yaml
new file mode 100644
index 0000000..73d061a
--- /dev/null
+++ b/manifests/jenkins/templates/jenkins-test.yaml
@@ -0,0 +1,45 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: "{{ .Release.Name }}-ui-test-{{ randAlphaNum 5 | lower }}"
+ annotations:
+ "helm.sh/hook": test-success
+spec:
+ {{- if .Values.Master.NodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.Master.NodeSelector | indent 4 }}
+ {{- end }}
+ {{- if .Values.Master.Tolerations }}
+ tolerations:
+{{ toYaml .Values.Master.Tolerations | indent 4 }}
+ {{- end }}
+ initContainers:
+ - name: "test-framework"
+ image: "dduportal/bats:0.4.0"
+ command:
+ - "bash"
+ - "-c"
+ - |
+ set -ex
+ # copy bats to tools dir
+ cp -R /usr/local/libexec/ /tools/bats/
+ volumeMounts:
+ - mountPath: /tools
+ name: tools
+ containers:
+ - name: {{ .Release.Name }}-ui-test
+ image: {{ .Values.Master.Image }}:{{ .Values.Master.ImageTag }}
+ command: ["/tools/bats/bats", "-t", "/tests/run.sh"]
+ volumeMounts:
+ - mountPath: /tests
+ name: tests
+ readOnly: true
+ - mountPath: /tools
+ name: tools
+ volumes:
+ - name: tests
+ configMap:
+ name: {{ template "jenkins.fullname" . }}-tests
+ - name: tools
+ emptyDir: {}
+ restartPolicy: Never
diff --git a/manifests/jenkins/templates/jobs.yaml b/manifests/jenkins/templates/jobs.yaml
new file mode 100644
index 0000000..c16813a
--- /dev/null
+++ b/manifests/jenkins/templates/jobs.yaml
@@ -0,0 +1,8 @@
+{{- if .Values.Master.Jobs }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "jenkins.fullname" . }}-jobs
+data:
+{{ .Values.Master.Jobs | indent 2 }}
+{{- end -}}
diff --git a/manifests/jenkins/templates/rbac.yaml b/manifests/jenkins/templates/rbac.yaml
new file mode 100644
index 0000000..5a828aa
--- /dev/null
+++ b/manifests/jenkins/templates/rbac.yaml
@@ -0,0 +1,20 @@
+{{ if .Values.rbac.install }}
+{{- $serviceName := include "jenkins.fullname" . -}}
+apiVersion: rbac.authorization.k8s.io/{{ required "A valid .Values.rbac.apiVersion entry required!" .Values.rbac.apiVersion }}
+kind: ClusterRoleBinding
+metadata:
+ name: {{ $serviceName }}-role-binding
+ labels:
+ app: {{ $serviceName }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ release: "{{ .Release.Name }}"
+ heritage: "{{ .Release.Service }}"
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: {{ .Values.rbac.roleRef }}
+subjects:
+- kind: ServiceAccount
+ name: {{ $serviceName }}
+ namespace: {{ .Release.Namespace }}
+{{ end }}
\ No newline at end of file
diff --git a/manifests/jenkins/templates/secret.yaml b/manifests/jenkins/templates/secret.yaml
new file mode 100644
index 0000000..47cc2e0
--- /dev/null
+++ b/manifests/jenkins/templates/secret.yaml
@@ -0,0 +1,19 @@
+{{- if .Values.Master.UseSecurity }}
+apiVersion: v1
+kind: Secret
+metadata:
+ name: {{ template "jenkins.fullname" . }}
+ labels:
+ app: {{ template "jenkins.fullname" . }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ release: "{{ .Release.Name }}"
+ heritage: "{{ .Release.Service }}"
+type: Opaque
+data:
+ {{ if .Values.Master.AdminPassword }}
+ jenkins-admin-password: {{ .Values.Master.AdminPassword | b64enc | quote }}
+ {{ else }}
+ jenkins-admin-password: {{ randAlphaNum 10 | b64enc | quote }}
+ {{ end }}
+ jenkins-admin-user: {{ .Values.Master.AdminUser | b64enc | quote }}
+{{- end }}
\ No newline at end of file
diff --git a/manifests/jenkins/templates/service-account.yaml b/manifests/jenkins/templates/service-account.yaml
new file mode 100644
index 0000000..cb0911c
--- /dev/null
+++ b/manifests/jenkins/templates/service-account.yaml
@@ -0,0 +1,12 @@
+{{ if .Values.rbac.install }}
+{{- $serviceName := include "jenkins.fullname" . -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ $serviceName }}
+ labels:
+ app: {{ $serviceName }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ release: "{{ .Release.Name }}"
+ heritage: "{{ .Release.Service }}"
+{{ end }}
\ No newline at end of file
diff --git a/manifests/jenkins/templates/test-config.yaml b/manifests/jenkins/templates/test-config.yaml
new file mode 100644
index 0000000..00b8a66
--- /dev/null
+++ b/manifests/jenkins/templates/test-config.yaml
@@ -0,0 +1,9 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "jenkins.fullname" . }}-tests
+data:
+ run.sh: |-
+ @test "Testing Jenkins UI is accessible" {
+ curl --retry 48 --retry-delay 10 {{ template "jenkins.fullname" . }}:{{ .Values.Master.ServicePort }}{{ default "" .Values.Master.JenkinsUriPrefix }}/login
+ }
diff --git a/manifests/jenkins/values.yaml b/manifests/jenkins/values.yaml
new file mode 100644
index 0000000..c2311e7
--- /dev/null
+++ b/manifests/jenkins/values.yaml
@@ -0,0 +1,210 @@
+# Default values for jenkins.
+# This is a YAML-formatted file.
+# Declare name/value pairs to be passed into your templates.
+# name: value
+
+## Overrides for generated resource names
+# See templates/_helpers.tpl
+# nameOverride:
+# fullnameOverride:
+
+Master:
+ Name: jenkins-master
+ Image: "jenkins/jenkins"
+ ImageTag: "2.138.2-alpine"
+ ImagePullPolicy: "IfNotPresent"
+# ImagePullSecret: jenkins
+ Component: "jenkins-master"
+ UseSecurity: true
+ AdminUser: admin
+ AdminPassword: admin
+ resources:
+ requests:
+ cpu: "50m"
+ memory: "256Mi"
+ limits:
+ cpu: "2000m"
+ memory: "2048Mi"
+ # Environment variables that get added to the init container (useful for e.g. http_proxy)
+ # InitContainerEnv:
+ # - name: http_proxy
+ # value: "http://192.168.64.1:3128"
+ # ContainerEnv:
+ # - name: http_proxy
+ # value: "http://192.168.64.1:3128"
+ # Set min/max heap here if needed with:
+ # JavaOpts: "-Xms512m -Xmx512m"
+ # JenkinsOpts: ""
+ # JenkinsUriPrefix: "/jenkins"
+
+ # Enable pod security context (must be `true` if RunAsUser or FsGroup are set)
+ # UsePodSecurityContext: true
+
+ # Set RunAsUser to 1000 to let Jenkins run as non-root user 'jenkins' which exists in 'jenkins/jenkins' docker image.
+ # When setting RunAsUser to a different value than 0 also set FsGroup to the same value:
+ # RunAsUser:
+ # FsGroup:
+ ServicePort: 8080
+ # For minikube, set this to NodePort, elsewhere use LoadBalancer
+ # Use ClusterIP if your setup includes ingress controller
+ ServiceType: ClusterIP
+ # Master Service annotations
+ ServiceAnnotations: {}
+ # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: https
+ # Used to create Ingress record (should used with ServiceType: ClusterIP)
+ HostName: jenkins.local.com
+ # NodePort:
+ -Djava.awt.headless=true
+ -Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Shanghai
+ -Dfile.encoding=UTF-8
+ # -Dcom.sun.management.jmxremote.port=4000
+ # -Dcom.sun.management.jmxremote.authenticate=false
+ # -Dcom.sun.management.jmxremote.ssl=false
+ # JMXPort: 4000
+
+ # 插件镜像地址
+ UpdateCenter: https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/current/update-center.json
+
+ # List of plugins to be install during Jenkins master start
+ InstallPlugins:
+ - kubernetes:1.13.5
+ - workflow-aggregator:2.5
+ - workflow-job:2.25
+ - credentials-binding:1.17
+ - git:3.9.1
+ - gitlab:1.5.10
+ # Used to approve a list of groovy functions in pipelines used the script-security plugin. Can be viewed under /scriptApproval
+ # ScriptApproval:
+ # - "method groovy.json.JsonSlurperClassic parseText java.lang.String"
+ # - "new groovy.json.JsonSlurperClassic"
+ # List of groovy init scripts to be executed during Jenkins master start
+ InitScripts:
+ # - |
+ # print 'adding global pipeline libraries, register properties, bootstrap jobs...'
+ # Kubernetes secret that contains a 'credentials.xml' for Jenkins
+ # CredentialsXmlSecret: jenkins-credentials
+ # Kubernetes secret that contains files to be put in the Jenkins 'secrets' directory,
+ # useful to manage encryption keys used for credentials.xml for instance (such as
+ # master.key and hudson.util.Secret)
+ # SecretsFilesSecret: jenkins-secrets
+ # Jenkins XML job configs to provision
+ # Jobs: |-
+ # test: |-
+ # <>
+ CustomConfigMap: false
+ # Node labels and tolerations for pod assignment
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
+ # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature
+ NodeSelector: {}
+
+ Tolerations: {}
+ PodAnnotations: {}
+
+ Ingress:
+ ApiVersion: extensions/v1beta1
+ Annotations:
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+
+ TLS:
+ # - secretName: jenkins.cluster.local
+ # hosts:
+ # - jenkins.cluster.local
+
+Agent:
+ Enabled: true
+ Image: jenkinsci/jnlp-slave
+ ImageTag: alpine
+# ImagePullSecret: jenkins
+ Component: "jenkins-slave"
+ Privileged: false
+ resources:
+ requests:
+ cpu: "200m"
+ memory: "256Mi"
+ limits:
+ cpu: "200m"
+ memory: "256Mi"
+ # You may want to change this to true while testing a new image
+ AlwaysPullImage: false
+ # You can define the volumes that you want to mount for this container
+ # Allowed types are: ConfigMap, EmptyDir, HostPath, Nfs, Pod, Secret
+ # Configure the attributes as they appear in the corresponding Java class for that type
+ # https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes
+ volumes:
+ # - type: Secret
+ # secretName: mysecret
+ # mountPath: /var/myapp/mysecret
+ NodeSelector: {}
+ # Key Value selectors. Ex:
+ # jenkins-agent: v1
+
+Persistence:
+ Enabled: true
+ ## A manually managed Persistent Volume and Claim
+ ## Requires Persistence.Enabled: true
+ ## If defined, PVC must be created manually before volume will be bound
+ # ExistingClaim:
+
+ ## jenkins data Persistent Volume Storage Class
+ ## If defined, storageClassName:
+ ## If set to "-", storageClassName: "", which disables dynamic provisioning
+ ## If undefined (the default) or set to null, no storageClassName spec is
+ ## set, choosing the default provisioner. (gp2 on AWS, standard on
+ ## GKE, AWS & OpenStack)
+ ##
+ StorageClass: "nfs-dynamic-class"
+
+ Annotations: {}
+ AccessMode: ReadWriteOnce
+ Size: 8Gi
+ volumes:
+ # - name: nothing
+ # emptyDir: {}
+ mounts:
+ # - mountPath: /var/nothing
+ # name: nothing
+ # readOnly: true
+
+NetworkPolicy:
+ # Enable creation of NetworkPolicy resources.
+ Enabled: false
+ # For Kubernetes v1.4, v1.5 and v1.6, use 'extensions/v1beta1'
+ # For Kubernetes v1.7, use 'networking.k8s.io/v1'
+ ApiVersion: extensions/v1beta1
+
+## Install Default RBAC roles and bindings
+rbac:
+ install: true
+ serviceAccountName: default
+ # RBAC api version (currently either v1beta1 or v1alpha1 or v1)
+ apiVersion: v1
+ # Cluster role reference
+ roleRef: cluster-admin
diff --git a/manifests/mariadb-cluster/mariadb/.helmignore b/manifests/mariadb-cluster/mariadb/.helmignore
new file mode 100644
index 0000000..6b8710a
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/.helmignore
@@ -0,0 +1 @@
+.git
diff --git a/manifests/mariadb-cluster/mariadb/Chart.yaml b/manifests/mariadb-cluster/mariadb/Chart.yaml
new file mode 100644
index 0000000..f105c37
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/Chart.yaml
@@ -0,0 +1,19 @@
+name: mariadb
+version: 5.5.0
+appVersion: 10.1.37
+description: Fast, reliable, scalable, and easy to use open-source relational database system. MariaDB Server is intended for mission-critical, heavy-load production systems as well as for embedding into mass-deployed software. Highly available MariaDB cluster.
+keywords:
+- mariadb
+- mysql
+- database
+- sql
+- prometheus
+home: https://mariadb.org
+icon: https://bitnami.com/assets/stacks/mariadb/img/mariadb-stack-220x234.png
+sources:
+- https://github.com/bitnami/bitnami-docker-mariadb
+- https://github.com/prometheus/mysqld_exporter
+maintainers:
+- name: Bitnami
+ email: containers@bitnami.com
+engine: gotpl
diff --git a/manifests/mariadb-cluster/mariadb/OWNERS b/manifests/mariadb-cluster/mariadb/OWNERS
new file mode 100644
index 0000000..2c3e9fa
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/OWNERS
@@ -0,0 +1,12 @@
+approvers:
+- prydonius
+- tompizmor
+- sameersbn
+- carrodher
+- juan131
+reviewers:
+- prydonius
+- tompizmor
+- sameersbn
+- carrodher
+- juan131
diff --git a/manifests/mariadb-cluster/mariadb/README.md b/manifests/mariadb-cluster/mariadb/README.md
new file mode 100644
index 0000000..16bf9a0
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/README.md
@@ -0,0 +1,205 @@
+# MariaDB
+
+[MariaDB](https://mariadb.org) is one of the most popular database servers in the world. It’s made by the original developers of MySQL and guaranteed to stay open source. Notable users include Wikipedia, Facebook and Google.
+
+MariaDB is developed as open source software and as a relational database it provides an SQL interface for accessing data. The latest versions of MariaDB also include GIS and JSON features.
+
+## TL;DR
+
+```bash
+$ helm install stable/mariadb
+```
+
+## Introduction
+
+This chart bootstraps a [MariaDB](https://github.com/bitnami/bitnami-docker-mariadb) replication cluster deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
+
+Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters.
+
+## Prerequisites
+
+- Kubernetes 1.10+
+- PV provisioner support in the underlying infrastructure
+
+## Installing the Chart
+
+To install the chart with the release name `my-release`:
+
+```bash
+$ helm install --name my-release stable/mariadb
+```
+
+The command deploys MariaDB on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation.
+
+> **Tip**: List all releases using `helm list`
+
+## Uninstalling the Chart
+
+To uninstall/delete the `my-release` deployment:
+
+```bash
+$ helm delete my-release
+```
+
+The command removes all the Kubernetes components associated with the chart and deletes the release.
+
+## Configuration
+
+The following table lists the configurable parameters of the MariaDB chart and their default values.
+
+| Parameter | Description | Default |
+|-------------------------------------------|-----------------------------------------------------|-------------------------------------------------------------------|
+| `global.imageRegistry` | Global Docker image registry | `nil` |
+| `image.registry` | MariaDB image registry | `docker.io` |
+| `image.repository` | MariaDB Image name | `bitnami/mariadb` |
+| `image.tag` | MariaDB Image tag | `{VERSION}` |
+| `image.pullPolicy` | MariaDB image pull policy | `Always` if `imageTag` is `latest`, else `IfNotPresent` |
+| `image.pullSecrets` | Specify docker-registry secret names as an array | `[]` (does not add image pull secrets to deployed pods) |
+| `image.debug` | Specify if debug logs should be enabled | `false` |
+| `service.type` | Kubernetes service type | `ClusterIP` |
+| `service.clusterIp` | Specific cluster IP when service type is cluster IP. Use None for headless service | `nil` |
+| `service.port` | MySQL service port | `3306` |
+| `serviceAccount.create` | Specifies whether a ServiceAccount should be created | `false` |
+| `serviceAccount.name` | The name of the ServiceAccount to create | Generated using the mariadb.fullname template |
+| `securityContext.enabled` | Enable security context | `true` |
+| `securityContext.fsGroup` | Group ID for the container | `1001` |
+| `securityContext.runAsUser` | User ID for the container | `1001` |
+| `existingSecret` | Use Existing secret for Password details (`rootUser.password`, `db.password`, `replication.password` will be ignored and picked up from this secret) | |
+| `rootUser.password` | Password for the `root` user. Ignored if existing secret is provided. | _random 10 character alphanumeric string_ |
+| `rootUser.forcePassword` | Force users to specify a password | `false` |
+| `db.user` | Username of new user to create | `nil` |
+| `db.password` | Password for the new user. Ignored if existing secret is provided. | _random 10 character alphanumeric string if `db.user` is defined_ |
+| `db.name` | Name for new database to create | `my_database` |
+| `replication.enabled` | MariaDB replication enabled | `true` |
+| `replication.user` |MariaDB replication user | `replicator` |
+| `replication.password` | MariaDB replication user password. Ignored if existing secret is provided. | _random 10 character alphanumeric string_ |
+| `initdbScripts` | List of initdb scripts | `nil` |
+| `initdbScriptsConfigMap` | ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) | `nil` |
+| `master.annotations[].key` | key for the the annotation list item | `nil` |
+| `master.annotations[].value` | value for the the annotation list item | `nil` |
+| `master.affinity` | Master affinity (in addition to master.antiAffinity when set) | `{}` |
+| `master.antiAffinity` | Master pod anti-affinity policy | `soft` |
+| `master.tolerations` | List of node taints to tolerate (master) | `[]` |
+| `master.persistence.enabled` | Enable persistence using PVC | `true` |
+| `master.persistence.existingClaim` | Provide an existing `PersistentVolumeClaim` | `nil` |
+| `master.persistence.mountPath` | Path to mount the volume at | `/bitnami/mariadb` |
+| `master.persistence.annotations` | Persistent Volume Claim annotations | `{}` |
+| `master.persistence.storageClass` | Persistent Volume Storage Class | `` |
+| `master.persistence.accessModes` | Persistent Volume Access Modes | `[ReadWriteOnce]` |
+| `master.persistence.size` | Persistent Volume Size | `8Gi` |
+| `master.extraInitContainers` | Additional init containers as a string to be passed to the `tpl` function (master) | |
+| `master.config` | Config file for the MariaDB Master server | `_default values in the values.yaml file_` |
+| `master.resources` | CPU/Memory resource requests/limits for master node | `{}` |
+| `master.livenessProbe.enabled` | Turn on and off liveness probe (master) | `true` |
+| `master.livenessProbe.initialDelaySeconds`| Delay before liveness probe is initiated (master) | `120` |
+| `master.livenessProbe.periodSeconds` | How often to perform the probe (master) | `10` |
+| `master.livenessProbe.timeoutSeconds` | When the probe times out (master) | `1` |
+| `master.livenessProbe.successThreshold` | Minimum consecutive successes for the probe (master)| `1` |
+| `master.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe (master) | `3` |
+| `master.readinessProbe.enabled` | Turn on and off readiness probe (master) | `true` |
+| `master.readinessProbe.initialDelaySeconds`| Delay before readiness probe is initiated (master) | `30` |
+| `master.readinessProbe.periodSeconds` | How often to perform the probe (master) | `10` |
+| `master.readinessProbe.timeoutSeconds` | When the probe times out (master) | `1` |
+| `master.readinessProbe.successThreshold` | Minimum consecutive successes for the probe (master)| `1` |
+| `master.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe (master) | `3` |
+| `slave.replicas` | Desired number of slave replicas | `1` |
+| `slave.annotations[].key` | key for the the annotation list item | `nil` |
+| `slave.annotations[].value` | value for the the annotation list item | `nil` |
+| `slave.affinity` | Slave affinity (in addition to slave.antiAffinity when set) | `{}` |
+| `slave.antiAffinity` | Slave pod anti-affinity policy | `soft` |
+| `slave.tolerations` | List of node taints to tolerate for (slave) | `[]` |
+| `slave.persistence.enabled` | Enable persistence using a `PersistentVolumeClaim` | `true` |
+| `slave.persistence.annotations` | Persistent Volume Claim annotations | `{}` |
+| `slave.persistence.storageClass` | Persistent Volume Storage Class | `` |
+| `slave.persistence.accessModes` | Persistent Volume Access Modes | `[ReadWriteOnce]` |
+| `slave.persistence.size` | Persistent Volume Size | `8Gi` |
+| `slave.extraInitContainers` | Additional init containers as a string to be passed to the `tpl` function (slave) | |
+| `slave.config` | Config file for the MariaDB Slave replicas | `_default values in the values.yaml file_` |
+| `slave.resources` | CPU/Memory resource requests/limits for slave node | `{}` |
+| `slave.livenessProbe.enabled` | Turn on and off liveness probe (slave) | `true` |
+| `slave.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated (slave) | `120` |
+| `slave.livenessProbe.periodSeconds` | How often to perform the probe (slave) | `10` |
+| `slave.livenessProbe.timeoutSeconds` | When the probe times out (slave) | `1` |
+| `slave.livenessProbe.successThreshold` | Minimum consecutive successes for the probe (slave) | `1` |
+| `slave.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe (slave) | `3` |
+| `slave.readinessProbe.enabled` | Turn on and off readiness probe (slave) | `true` |
+| `slave.readinessProbe.initialDelaySeconds`| Delay before readiness probe is initiated (slave) | `45` |
+| `slave.readinessProbe.periodSeconds` | How often to perform the probe (slave) | `10` |
+| `slave.readinessProbe.timeoutSeconds` | When the probe times out (slave) | `1` |
+| `slave.readinessProbe.successThreshold` | Minimum consecutive successes for the probe (slave) | `1` |
+| `slave.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe (slave) | `3` |
+| `metrics.enabled` | Start a side-car prometheus exporter | `false` |
+| `metrics.image.registry` | Exporter image registry | `docker.io` |
+| `metrics.image.repository` | Exporter image name | `prom/mysqld-exporter` |
+| `metrics.image.tag` | Exporter image tag | `v0.10.0` |
+| `metrics.image.pullPolicy` | Exporter image pull policy | `IfNotPresent` |
+| `metrics.resources` | Exporter resource requests/limit | `nil` |
+
+The above parameters map to the env variables defined in [bitnami/mariadb](http://github.com/bitnami/bitnami-docker-mariadb). For more information please refer to the [bitnami/mariadb](http://github.com/bitnami/bitnami-docker-mariadb) image documentation.
+
+Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
+
+```bash
+$ helm install --name my-release \
+ --set root.password=secretpassword,user.database=app_database \
+ stable/mariadb
+```
+
+The above command sets the MariaDB `root` account password to `secretpassword`. Additionally it creates a database named `my_database`.
+
+Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example,
+
+```bash
+$ helm install --name my-release -f values.yaml stable/mariadb
+```
+
+> **Tip**: You can use the default [values.yaml](values.yaml)
+
+## Initialize a fresh instance
+
+The [Bitnami MariaDB](https://github.com/bitnami/bitnami-docker-mariadb) image allows you to use your custom scripts to initialize a fresh instance. In order to execute the scripts, they must be located inside the chart folder `files/docker-entrypoint-initdb.d` so they can be consumed as a ConfigMap.
+
+Alternatively, you can specify custom scripts using the `initdbScripts` parameter as dict.
+
+In addition to these options, you can also set an external ConfigMap with all the initialization scripts. This is done by setting the `initdbScriptsConfigMap` parameter. Note that this will override the two previous options.
+
+The allowed extensions are `.sh`, `.sql` and `.sql.gz`.
+
+## Persistence
+
+The [Bitnami MariaDB](https://github.com/bitnami/bitnami-docker-mariadb) image stores the MariaDB data and configurations at the `/bitnami/mariadb` path of the container.
+
+The chart mounts a [Persistent Volume](kubernetes.io/docs/user-guide/persistent-volumes/) volume at this location. The volume is created using dynamic volume provisioning, by default. An existing PersistentVolumeClaim can be defined.
+
+## Extra Init Containers
+
+The feature allows for specifying a template string for a initContainer in the master/slave pod. Usecases include situations when you need some pre-run setup. For example, in IKS (IBM Cloud Kubernetes Service), non-root users do not have write permission on the volume mount path for NFS-powered file storage. So, you could use a initcontainer to `chown` the mount. See a example below, where we add an initContainer on the master pod that reports to an external resource that the db is going to starting.
+`values.yaml`
+```yaml
+master:
+ extraInitContainers: |
+ - name: initcontainer
+ image: alpine:latest
+ command: ["/bin/sh", "-c"]
+ args:
+ - curl http://api-service.local/db/starting;
+```
+
+## Upgrading
+
+It's necessary to set the `rootUser.password` parameter when upgrading for readiness/liveness probes to work properly. When you install this chart for the first time, some notes will be displayed providing the credentials you must use under the 'Administrator credentials' section. Please note down the password and run the command below to upgrade your chart:
+
+```bash
+$ helm upgrade my-release stable/mariadb --set rootUser.password=[ROOT_PASSWORD]
+```
+
+| Note: you need to substitute the placeholder _[ROOT_PASSWORD]_ with the value obtained in the installation notes.
+
+### To 5.0.0
+
+Backwards compatibility is not guaranteed unless you modify the labels used on the chart's deployments.
+Use the workaround below to upgrade from versions previous to 5.0.0. The following example assumes that the release name is mariadb:
+
+```console
+$ kubectl delete statefulset opencart-mariadb --cascade=false
+```
diff --git a/manifests/mariadb-cluster/mariadb/files/docker-entrypoint-initdb.d/README.md b/manifests/mariadb-cluster/mariadb/files/docker-entrypoint-initdb.d/README.md
new file mode 100644
index 0000000..aaddde3
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/files/docker-entrypoint-initdb.d/README.md
@@ -0,0 +1,3 @@
+You can copy here your custom .sh, .sql or .sql.gz file so they are executed during the first boot of the image.
+
+More info in the [bitnami-docker-mariadb](https://github.com/bitnami/bitnami-docker-mariadb#initializing-a-new-instance) repository.
\ No newline at end of file
diff --git a/manifests/mariadb-cluster/mariadb/templates/NOTES.txt b/manifests/mariadb-cluster/mariadb/templates/NOTES.txt
new file mode 100644
index 0000000..768f77f
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/NOTES.txt
@@ -0,0 +1,42 @@
+
+Please be patient while the chart is being deployed
+
+Tip:
+
+ Watch the deployment status using the command: kubectl get pods -w --namespace {{ .Release.Namespace }} -l release={{ .Release.Name }}
+
+Services:
+
+ echo Master: {{ template "mariadb.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }}
+{{- if .Values.replication.enabled }}
+ echo Slave: {{ template "slave.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }}
+{{- end }}
+
+Administrator credentials:
+
+ Username: root
+ Password : $(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "mariadb.fullname" . }} -o jsonpath="{.data.mariadb-root-password}" | base64 --decode)
+
+To connect to your database:
+
+ 1. Run a pod that you can use as a client:
+
+ kubectl run {{ template "mariadb.fullname" . }}-client --rm --tty -i --restart='Never' --image {{ template "mariadb.image" . }} --namespace {{ .Release.Namespace }} --command -- bash
+
+ 2. To connect to master service (read/write):
+
+ mysql -h {{ template "mariadb.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local -uroot -p {{ .Values.db.name }}
+
+{{- if .Values.replication.enabled }}
+
+ 3. To connect to slave service (read-only):
+
+ mysql -h {{ template "slave.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local -uroot -p {{ .Values.db.name }}
+{{- end }}
+
+To upgrade this helm chart:
+
+ 1. Obtain the password as described on the 'Administrator credentials' section and set the 'rootUser.password' parameter as shown below:
+
+ ROOT_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "mariadb.fullname" . }} -o jsonpath="{.data.mariadb-root-password}" | base64 --decode)
+ helm upgrade {{ .Release.Name }} stable/mariadb --set rootUser.password=$ROOT_PASSWORD
diff --git a/manifests/mariadb-cluster/mariadb/templates/_helpers.tpl b/manifests/mariadb-cluster/mariadb/templates/_helpers.tpl
new file mode 100644
index 0000000..99ff780
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/_helpers.tpl
@@ -0,0 +1,97 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "mariadb.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "mariadb.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- printf .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "master.fullname" -}}
+{{- if .Values.replication.enabled -}}
+{{- printf "%s-%s" .Release.Name "mariadb-master" | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name "mariadb" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "slave.fullname" -}}
+{{- printf "%s-%s" .Release.Name "mariadb-slave" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{- define "mariadb.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Return the proper MariaDB image name
+*/}}
+{{- define "mariadb.image" -}}
+{{- $registryName := .Values.image.registry -}}
+{{- $repositoryName := .Values.image.repository -}}
+{{- $tag := .Values.image.tag | toString -}}
+{{/*
+Helm 2.11 supports the assignment of a value to a variable defined in a different scope,
+but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic.
+Also, we can't use a single if because lazy evaluation is not an option
+*/}}
+{{- if .Values.global }}
+ {{- if .Values.global.imageRegistry }}
+ {{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}}
+ {{- else -}}
+ {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}}
+ {{- end -}}
+{{- else -}}
+ {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Return the proper metrics image name
+*/}}
+{{- define "metrics.image" -}}
+{{- $registryName := .Values.metrics.image.registry -}}
+{{- $repositoryName := .Values.metrics.image.repository -}}
+{{- $tag := .Values.metrics.image.tag | toString -}}
+{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}}
+{{- end -}}
+
+{{ template "mariadb.initdbScriptsCM" . }}
+{{/*
+Get the initialization scripts ConfigMap name.
+*/}}
+{{- define "mariadb.initdbScriptsCM" -}}
+{{- if .Values.initdbScriptsConfigMap -}}
+{{- printf "%s" .Values.initdbScriptsConfigMap -}}
+{{- else -}}
+{{- printf "%s-init-scripts" (include "mariadb.fullname" .) -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "mariadb.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create -}}
+ {{ default (include "mariadb.fullname" .) .Values.serviceAccount.name }}
+{{- else -}}
+ {{ default "default" .Values.serviceAccount.name }}
+{{- end -}}
+{{- end -}}
diff --git a/manifests/mariadb-cluster/mariadb/templates/initialization-configmap.yaml b/manifests/mariadb-cluster/mariadb/templates/initialization-configmap.yaml
new file mode 100644
index 0000000..f7380af
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/initialization-configmap.yaml
@@ -0,0 +1,26 @@
+{{- if and (or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScripts) (not .Values.initdbScriptsConfigMap) }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "master.fullname" . }}-init-scripts
+ labels:
+ app: {{ template "mariadb.name" . }}
+ chart: {{ template "mariadb.chart" . }}
+ release: {{ .Release.Name | quote }}
+ heritage: {{ .Release.Service | quote }}
+ component: "master"
+{{- if and (.Files.Glob "files/docker-entrypoint-initdb.d/*.sql.gz") (not .Values.initdbScriptsConfigMap) }}
+binaryData:
+{{- $root := . }}
+{{- range $path, $bytes := .Files.Glob "files/docker-entrypoint-initdb.d/*.sql.gz" }}
+ {{ base $path }}: {{ $root.Files.Get $path | b64enc | quote }}
+{{- end }}
+{{- end }}
+data:
+{{- if and (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql}") (not .Values.initdbScriptsConfigMap) }}
+{{ (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql}").AsConfig | indent 2 }}
+{{- end }}
+{{- with .Values.initdbScripts }}
+{{ toYaml . | indent 2 }}
+{{- end }}
+{{ end }}
diff --git a/manifests/mariadb-cluster/mariadb/templates/master-configmap.yaml b/manifests/mariadb-cluster/mariadb/templates/master-configmap.yaml
new file mode 100644
index 0000000..880a101
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/master-configmap.yaml
@@ -0,0 +1,15 @@
+{{- if .Values.master.config }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "master.fullname" . }}
+ labels:
+ app: {{ template "mariadb.name" . }}
+ component: "master"
+ chart: {{ template "mariadb.chart" . }}
+ release: {{ .Release.Name | quote }}
+ heritage: {{ .Release.Service | quote }}
+data:
+ my.cnf: |-
+{{ .Values.master.config | indent 4 }}
+{{- end -}}
diff --git a/manifests/mariadb-cluster/mariadb/templates/master-statefulset.yaml b/manifests/mariadb-cluster/mariadb/templates/master-statefulset.yaml
new file mode 100644
index 0000000..c077a3b
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/master-statefulset.yaml
@@ -0,0 +1,247 @@
+apiVersion: apps/v1beta1
+kind: StatefulSet
+metadata:
+ name: {{ template "master.fullname" . }}
+ labels:
+ app: "{{ template "mariadb.name" . }}"
+ chart: {{ template "mariadb.chart" . }}
+ component: "master"
+ release: {{ .Release.Name | quote }}
+ heritage: {{ .Release.Service | quote }}
+spec:
+ selector:
+ matchLabels:
+ release: "{{ .Release.Name }}"
+ component: "master"
+ app: {{ template "mariadb.name" . }}
+ serviceName: "{{ template "master.fullname" . }}"
+ replicas: 1
+ updateStrategy:
+ type: RollingUpdate
+ template:
+ metadata:
+ {{- if .Values.master.annotations }}
+ annotations:
+ {{- range .Values.master.annotations }}
+ {{ .key }}: '{{ .value }}'
+ {{- end }}
+ {{- end }}
+ labels:
+ app: "{{ template "mariadb.name" . }}"
+ component: "master"
+ release: "{{ .Release.Name }}"
+ chart: {{ template "mariadb.chart" . }}
+ spec:
+ serviceAccountName: "{{ template "mariadb.serviceAccountName" . }}"
+ {{- if .Values.securityContext.enabled }}
+ securityContext:
+ fsGroup: {{ .Values.securityContext.fsGroup }}
+ runAsUser: {{ .Values.securityContext.runAsUser }}
+ {{- end }}
+ {{- if eq .Values.master.antiAffinity "hard" }}
+ affinity:
+ {{- with .Values.master.affinity }}
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ podAntiAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - topologyKey: "kubernetes.io/hostname"
+ labelSelector:
+ matchLabels:
+ app: "{{ template "mariadb.name" . }}"
+ release: "{{ .Release.Name }}"
+ {{- else if eq .Values.master.antiAffinity "soft" }}
+ affinity:
+ {{- with .Values.master.affinity }}
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ podAntiAffinity:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - weight: 1
+ podAffinityTerm:
+ topologyKey: kubernetes.io/hostname
+ labelSelector:
+ matchLabels:
+ app: "{{ template "mariadb.name" . }}"
+ release: "{{ .Release.Name }}"
+ {{- else}}
+ {{- with .Values.master.affinity }}
+ affinity:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- end }}
+ {{- with .Values.master.tolerations }}
+ tolerations:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- if .Values.image.pullSecrets }}
+ imagePullSecrets:
+ {{- range .Values.image.pullSecrets }}
+ - name: {{ . }}
+ {{- end}}
+ {{- end }}
+ {{- if .Values.master.extraInitContainers }}
+ initContainers:
+{{ tpl .Values.master.extraInitContainers . | indent 6}}
+ {{- end }}
+ containers:
+ - name: "mariadb"
+ image: {{ template "mariadb.image" . }}
+ imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
+ env:
+ {{- if .Values.image.debug}}
+ - name: BITNAMI_DEBUG
+ value: "true"
+ {{- end }}
+ - name: MARIADB_ROOT_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "mariadb.fullname" . }}
+ {{- end }}
+ key: mariadb-root-password
+ {{- if .Values.db.user }}
+ - name: MARIADB_USER
+ value: "{{ .Values.db.user }}"
+ - name: MARIADB_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "mariadb.fullname" . }}
+ {{- end }}
+ key: mariadb-password
+ {{- end }}
+ - name: MARIADB_DATABASE
+ value: "{{ .Values.db.name }}"
+ {{- if .Values.replication.enabled }}
+ - name: MARIADB_REPLICATION_MODE
+ value: "master"
+ - name: MARIADB_REPLICATION_USER
+ value: "{{ .Values.replication.user }}"
+ - name: MARIADB_REPLICATION_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "mariadb.fullname" . }}
+ {{- end }}
+ key: mariadb-replication-password
+ {{- end }}
+ ports:
+ - name: mysql
+ containerPort: 3306
+ {{- if .Values.master.livenessProbe.enabled }}
+ livenessProbe:
+ exec:
+ command: ["sh", "-c", "exec mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD"]
+ initialDelaySeconds: {{ .Values.master.livenessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.master.livenessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.master.livenessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.master.livenessProbe.successThreshold }}
+ failureThreshold: {{ .Values.master.livenessProbe.failureThreshold }}
+ {{- end }}
+ {{- if .Values.master.readinessProbe.enabled }}
+ readinessProbe:
+ exec:
+ command: ["sh", "-c", "exec mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD"]
+ initialDelaySeconds: {{ .Values.master.readinessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.master.readinessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.master.readinessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.master.readinessProbe.successThreshold }}
+ failureThreshold: {{ .Values.master.readinessProbe.failureThreshold }}
+ {{- end }}
+ resources:
+{{ toYaml .Values.master.resources | indent 10 }}
+ volumeMounts:
+ - name: data
+ mountPath: {{ .Values.master.persistence.mountPath }}
+ {{- if or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScriptsConfigMap .Values.initdbScripts }}
+ - name: custom-init-scripts
+ mountPath: /docker-entrypoint-initdb.d
+ {{- end }}
+ {{- if .Values.master.config }}
+ - name: config
+ mountPath: /opt/bitnami/mariadb/conf/my.cnf
+ subPath: my.cnf
+ {{- end }}
+{{- if .Values.metrics.enabled }}
+ - name: metrics
+ image: {{ template "metrics.image" . }}
+ imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }}
+ env:
+ - name: MARIADB_ROOT_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "mariadb.fullname" . }}
+ {{- end }}
+ key: mariadb-root-password
+ command: [ 'sh', '-c', 'DATA_SOURCE_NAME="root:$MARIADB_ROOT_PASSWORD@(localhost:3306)/" /bin/mysqld_exporter' ]
+ ports:
+ - name: metrics
+ containerPort: 9104
+ livenessProbe:
+ httpGet:
+ path: /metrics
+ port: metrics
+ initialDelaySeconds: 15
+ timeoutSeconds: 5
+ readinessProbe:
+ httpGet:
+ path: /metrics
+ port: metrics
+ initialDelaySeconds: 5
+ timeoutSeconds: 1
+ resources:
+{{ toYaml .Values.metrics.resources | indent 10 }}
+{{- end }}
+ volumes:
+ {{- if .Values.master.config }}
+ - name: config
+ configMap:
+ name: {{ template "master.fullname" . }}
+ {{- end }}
+ {{- if or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScriptsConfigMap .Values.initdbScripts }}
+ - name: custom-init-scripts
+ configMap:
+ name: {{ template "mariadb.initdbScriptsCM" . }}
+ {{- end }}
+{{- if and .Values.master.persistence.enabled .Values.master.persistence.existingClaim }}
+ - name: data
+ persistentVolumeClaim:
+ claimName: {{ .Values.master.persistence.existingClaim }}
+{{- else if not .Values.master.persistence.enabled }}
+ - name: data
+ emptyDir: {}
+{{- else if and .Values.master.persistence.enabled (not .Values.master.persistence.existingClaim) }}
+ volumeClaimTemplates:
+ - metadata:
+ name: data
+ labels:
+ app: "{{ template "mariadb.name" . }}"
+ component: "master"
+ release: {{ .Release.Name | quote }}
+ heritage: {{ .Release.Service | quote }}
+ spec:
+ accessModes:
+ {{- range .Values.master.persistence.accessModes }}
+ - {{ . | quote }}
+ {{- end }}
+ resources:
+ requests:
+ storage: {{ .Values.master.persistence.size | quote }}
+ {{- if .Values.master.persistence.storageClass }}
+ {{- if (eq "-" .Values.master.persistence.storageClass) }}
+ storageClassName: ""
+ {{- else }}
+ storageClassName: {{ .Values.master.persistence.storageClass | quote }}
+ {{- end }}
+ {{- end }}
+{{- end }}
diff --git a/manifests/mariadb-cluster/mariadb/templates/master-svc.yaml b/manifests/mariadb-cluster/mariadb/templates/master-svc.yaml
new file mode 100644
index 0000000..56810b4
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/master-svc.yaml
@@ -0,0 +1,41 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ template "mariadb.fullname" . }}
+ labels:
+ app: "{{ template "mariadb.name" . }}"
+ component: "master"
+ chart: {{ template "mariadb.chart" . }}
+ release: {{ .Release.Name | quote }}
+ heritage: {{ .Release.Service | quote }}
+{{- if .Values.metrics.enabled }}
+ annotations:
+{{ toYaml .Values.metrics.annotations | indent 4 }}
+{{- end }}
+spec:
+ type: {{ .Values.service.type }}
+ {{- if eq .Values.service.type "ClusterIP" }}
+ {{- if .Values.service.clusterIp }}
+ clusterIP: {{ .Values.service.clusterIp }}
+ {{- end }}
+ {{- end }}
+ ports:
+ - name: mysql
+ port: {{ .Values.service.port }}
+ targetPort: mysql
+{{- if eq .Values.service.type "NodePort" }}
+{{- if .Values.service.nodePort }}
+{{- if .Values.service.nodePort.master }}
+ nodePort: {{ .Values.service.nodePort.master }}
+{{- end }}
+{{- end }}
+{{- end }}
+{{- if .Values.metrics.enabled }}
+ - name: metrics
+ port: 9104
+ targetPort: metrics
+{{- end }}
+ selector:
+ app: "{{ template "mariadb.name" . }}"
+ component: "master"
+ release: "{{ .Release.Name }}"
diff --git a/manifests/mariadb-cluster/mariadb/templates/secrets.yaml b/manifests/mariadb-cluster/mariadb/templates/secrets.yaml
new file mode 100644
index 0000000..401691c
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/secrets.yaml
@@ -0,0 +1,38 @@
+{{- if (not .Values.existingSecret) -}}
+apiVersion: v1
+kind: Secret
+metadata:
+ name: {{ template "mariadb.fullname" . }}
+ labels:
+ app: "{{ template "mariadb.name" . }}"
+ chart: {{ template "mariadb.chart" . }}
+ release: {{ .Release.Name | quote }}
+ heritage: {{ .Release.Service | quote }}
+type: Opaque
+data:
+ {{- if .Values.rootUser.password }}
+ mariadb-root-password: "{{ .Values.rootUser.password | b64enc }}"
+ {{- else if (not .Values.rootUser.forcePassword) }}
+ mariadb-root-password: "{{ randAlphaNum 10 | b64enc }}"
+ {{ else }}
+ mariadb-root-password: {{ required "A MariaDB Root Password is required!" .Values.rootUser.password }}
+ {{- end }}
+ {{- if .Values.db.user }}
+ {{- if .Values.db.password }}
+ mariadb-password: "{{ .Values.db.password | b64enc }}"
+ {{- else if (not .Values.db.forcePassword) }}
+ mariadb-password: "{{ randAlphaNum 10 | b64enc }}"
+ {{- else }}
+ mariadb-password: {{ required "A MariaDB Database Password is required!" .Values.db.password }}
+ {{- end }}
+ {{- end }}
+ {{- if .Values.replication.enabled }}
+ {{- if .Values.replication.password }}
+ mariadb-replication-password: "{{ .Values.replication.password | b64enc }}"
+ {{- else if (not .Values.replication.forcePassword) }}
+ mariadb-replication-password: "{{ randAlphaNum 10 | b64enc }}"
+ {{- else }}
+ mariadb-replication-password: {{ required "A MariaDB Replication Password is required!" .Values.replication.password }}
+ {{- end }}
+ {{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/manifests/mariadb-cluster/mariadb/templates/slave-configmap.yaml b/manifests/mariadb-cluster/mariadb/templates/slave-configmap.yaml
new file mode 100644
index 0000000..056cf5c
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/slave-configmap.yaml
@@ -0,0 +1,15 @@
+{{- if and .Values.replication.enabled .Values.slave.config }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "slave.fullname" . }}
+ labels:
+ app: {{ template "mariadb.name" . }}
+ component: "slave"
+ chart: {{ template "mariadb.chart" . }}
+ release: {{ .Release.Name | quote }}
+ heritage: {{ .Release.Service | quote }}
+data:
+ my.cnf: |-
+{{ .Values.slave.config | indent 4 }}
+{{- end }}
diff --git a/manifests/mariadb-cluster/mariadb/templates/slave-statefulset.yaml b/manifests/mariadb-cluster/mariadb/templates/slave-statefulset.yaml
new file mode 100644
index 0000000..f2cab61
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/slave-statefulset.yaml
@@ -0,0 +1,225 @@
+{{- if .Values.replication.enabled }}
+apiVersion: apps/v1beta1
+kind: StatefulSet
+metadata:
+ name: {{ template "slave.fullname" . }}
+ labels:
+ app: "{{ template "mariadb.name" . }}"
+ chart: {{ template "mariadb.chart" . }}
+ component: "slave"
+ release: {{ .Release.Name | quote }}
+ heritage: {{ .Release.Service | quote }}
+spec:
+ selector:
+ matchLabels:
+ release: "{{ .Release.Name }}"
+ component: "slave"
+ app: {{ template "mariadb.name" . }}
+ serviceName: "{{ template "slave.fullname" . }}"
+ replicas: {{ .Values.slave.replicas }}
+ updateStrategy:
+ type: RollingUpdate
+ template:
+ metadata:
+ {{- if .Values.slave.annotations }}
+ annotations:
+ {{- range .Values.slave.annotations }}
+ {{ .key }}: '{{ .value }}'
+ {{- end }}
+ {{- end }}
+ labels:
+ app: "{{ template "mariadb.name" . }}"
+ component: "slave"
+ release: "{{ .Release.Name }}"
+ chart: {{ template "mariadb.chart" . }}
+ spec:
+ serviceAccountName: "{{ template "mariadb.serviceAccountName" . }}"
+ {{- if .Values.securityContext.enabled }}
+ securityContext:
+ fsGroup: {{ .Values.securityContext.fsGroup }}
+ runAsUser: {{ .Values.securityContext.runAsUser }}
+ {{- end }}
+ {{- if eq .Values.slave.antiAffinity "hard" }}
+ affinity:
+ {{- with .Values.slave.affinity }}
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ podAntiAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - topologyKey: "kubernetes.io/hostname"
+ labelSelector:
+ matchLabels:
+ app: "{{ template "mariadb.name" . }}"
+ release: "{{ .Release.Name }}"
+ {{- else if eq .Values.slave.antiAffinity "soft" }}
+ affinity:
+ {{- with .Values.slave.affinity }}
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ podAntiAffinity:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - weight: 1
+ podAffinityTerm:
+ topologyKey: kubernetes.io/hostname
+ labelSelector:
+ matchLabels:
+ app: "{{ template "mariadb.name" . }}"
+ release: "{{ .Release.Name }}"
+ {{- else}}
+ {{- with .Values.slave.affinity }}
+ affinity:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- end }}
+ {{- with .Values.slave.tolerations }}
+ tolerations:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- if .Values.image.pullSecrets }}
+ imagePullSecrets:
+ {{- range .Values.image.pullSecrets }}
+ - name: {{ . }}
+ {{- end}}
+ {{- end }}
+ {{- if .Values.master.extraInitContainers }}
+ initContainers:
+{{ tpl .Values.master.extraInitContainers . | indent 6}}
+ {{- end }}
+ containers:
+ - name: "mariadb"
+ image: {{ template "mariadb.image" . }}
+ imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
+ env:
+ {{- if .Values.image.debug}}
+ - name: BITNAMI_DEBUG
+ value: "true"
+ {{- end }}
+ - name: MARIADB_REPLICATION_MODE
+ value: "slave"
+ - name: MARIADB_MASTER_HOST
+ value: {{ template "mariadb.fullname" . }}
+ - name: MARIADB_MASTER_PORT_NUMBER
+ value: "3306"
+ - name: MARIADB_MASTER_ROOT_USER
+ value: "root"
+ - name: MARIADB_MASTER_ROOT_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "mariadb.fullname" . }}
+ {{- end }}
+ key: mariadb-root-password
+ - name: MARIADB_REPLICATION_USER
+ value: "{{ .Values.replication.user }}"
+ - name: MARIADB_REPLICATION_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "mariadb.fullname" . }}
+ {{- end }}
+ key: mariadb-replication-password
+ ports:
+ - name: mysql
+ containerPort: 3306
+ {{- if .Values.slave.livenessProbe.enabled }}
+ livenessProbe:
+ exec:
+ command: ["sh", "-c", "exec mysqladmin status -uroot -p$MARIADB_MASTER_ROOT_PASSWORD"]
+ initialDelaySeconds: {{ .Values.slave.livenessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.slave.livenessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.slave.livenessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.slave.livenessProbe.successThreshold }}
+ failureThreshold: {{ .Values.slave.livenessProbe.failureThreshold }}
+ {{- end }}
+ {{- if .Values.slave.readinessProbe.enabled }}
+ readinessProbe:
+ exec:
+ command: ["sh", "-c", "exec mysqladmin status -uroot -p$MARIADB_MASTER_ROOT_PASSWORD"]
+ initialDelaySeconds: {{ .Values.slave.readinessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.slave.readinessProbe.periodSeconds }}
+ timeoutSeconds: {{ .Values.slave.readinessProbe.timeoutSeconds }}
+ successThreshold: {{ .Values.slave.readinessProbe.successThreshold }}
+ failureThreshold: {{ .Values.slave.readinessProbe.failureThreshold }}
+ {{- end }}
+ resources:
+{{ toYaml .Values.slave.resources | indent 10 }}
+ volumeMounts:
+ - name: data
+ mountPath: /bitnami/mariadb
+{{- if .Values.slave.config }}
+ - name: config
+ mountPath: /opt/bitnami/mariadb/conf/my.cnf
+ subPath: my.cnf
+{{- end }}
+{{- if .Values.metrics.enabled }}
+ - name: metrics
+ image: {{ template "metrics.image" . }}
+ imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }}
+ env:
+ - name: MARIADB_MASTER_ROOT_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "mariadb.fullname" . }}
+ {{- end }}
+ key: mariadb-root-password
+ command: [ 'sh', '-c', 'DATA_SOURCE_NAME="root:$MARIADB_MASTER_ROOT_PASSWORD@(localhost:3306)/" /bin/mysqld_exporter' ]
+ ports:
+ - name: metrics
+ containerPort: 9104
+ livenessProbe:
+ httpGet:
+ path: /metrics
+ port: metrics
+ initialDelaySeconds: 15
+ timeoutSeconds: 5
+ readinessProbe:
+ httpGet:
+ path: /metrics
+ port: metrics
+ initialDelaySeconds: 5
+ timeoutSeconds: 1
+ resources:
+{{ toYaml .Values.metrics.resources | indent 10 }}
+{{- end }}
+ volumes:
+ {{- if .Values.slave.config }}
+ - name: config
+ configMap:
+ name: {{ template "slave.fullname" . }}
+ {{- end }}
+{{- if .Values.slave.persistence.enabled }}
+ volumeClaimTemplates:
+ - metadata:
+ name: data
+ labels:
+ app: "{{ template "mariadb.name" . }}"
+ component: "slave"
+ release: {{ .Release.Name | quote }}
+ heritage: {{ .Release.Service | quote }}
+ spec:
+ accessModes:
+ {{- range .Values.slave.persistence.accessModes }}
+ - {{ . | quote }}
+ {{- end }}
+ resources:
+ requests:
+ storage: {{ .Values.slave.persistence.size | quote }}
+ {{- if .Values.slave.persistence.storageClass }}
+ {{- if (eq "-" .Values.slave.persistence.storageClass) }}
+ storageClassName: ""
+ {{- else }}
+ storageClassName: {{ .Values.slave.persistence.storageClass | quote }}
+ {{- end }}
+ {{- end }}
+{{- else }}
+ - name: "data"
+ emptyDir: {}
+{{- end }}
+{{- end }}
diff --git a/manifests/mariadb-cluster/mariadb/templates/slave-svc.yaml b/manifests/mariadb-cluster/mariadb/templates/slave-svc.yaml
new file mode 100644
index 0000000..c41ecb7
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/slave-svc.yaml
@@ -0,0 +1,43 @@
+{{- if .Values.replication.enabled }}
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ template "slave.fullname" . }}
+ labels:
+ app: "{{ template "mariadb.name" . }}"
+ chart: {{ template "mariadb.chart" . }}
+ component: "slave"
+ release: {{ .Release.Name | quote }}
+ heritage: {{ .Release.Service | quote }}
+{{- if .Values.metrics.enabled }}
+ annotations:
+{{ toYaml .Values.metrics.annotations | indent 4 }}
+{{- end }}
+spec:
+ type: {{ .Values.service.type }}
+ {{- if eq .Values.service.type "ClusterIP" }}
+ {{- if .Values.service.clusterIp }}
+ clusterIP: {{ .Values.service.clusterIp }}
+ {{- end }}
+ {{- end }}
+ ports:
+ - name: mysql
+ port: {{ .Values.service.port }}
+ targetPort: mysql
+{{- if (eq .Values.service.type "NodePort") }}
+{{- if .Values.service.nodePort }}
+{{- if .Values.service.nodePort.slave }}
+ nodePort: {{ .Values.service.nodePort.slave }}
+{{- end }}
+{{- end }}
+{{- end }}
+{{- if .Values.metrics.enabled }}
+ - name: metrics
+ port: 9104
+ targetPort: metrics
+{{- end }}
+ selector:
+ app: "{{ template "mariadb.name" . }}"
+ component: "slave"
+ release: "{{ .Release.Name }}"
+{{- end }}
diff --git a/manifests/mariadb-cluster/mariadb/templates/test-runner.yaml b/manifests/mariadb-cluster/mariadb/templates/test-runner.yaml
new file mode 100644
index 0000000..c9821ce
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/test-runner.yaml
@@ -0,0 +1,48 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: "{{ template "mariadb.fullname" . }}-test-{{ randAlphaNum 5 | lower }}"
+ annotations:
+ "helm.sh/hook": test-success
+spec:
+ initContainers:
+ - name: "test-framework"
+ image: "dduportal/bats:0.4.0"
+ command:
+ - "bash"
+ - "-c"
+ - |
+ set -ex
+ # copy bats to tools dir
+ cp -R /usr/local/libexec/ /tools/bats/
+ volumeMounts:
+ - mountPath: /tools
+ name: tools
+ containers:
+ - name: mariadb-test
+ image: {{ template "mariadb.image" . }}
+ imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
+ command: ["/tools/bats/bats", "-t", "/tests/run.sh"]
+ env:
+ - name: MARIADB_ROOT_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "mariadb.fullname" . }}
+ {{- end }}
+ key: mariadb-root-password
+ volumeMounts:
+ - mountPath: /tests
+ name: tests
+ readOnly: true
+ - mountPath: /tools
+ name: tools
+ volumes:
+ - name: tests
+ configMap:
+ name: {{ template "mariadb.fullname" . }}-tests
+ - name: tools
+ emptyDir: {}
+ restartPolicy: Never
diff --git a/manifests/mariadb-cluster/mariadb/templates/tests.yaml b/manifests/mariadb-cluster/mariadb/templates/tests.yaml
new file mode 100644
index 0000000..957f3fd
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/templates/tests.yaml
@@ -0,0 +1,9 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "mariadb.fullname" . }}-tests
+data:
+ run.sh: |-
+ @test "Testing MariaDB is accessible" {
+ mysql -h {{ template "mariadb.fullname" . }} -uroot -p$MARIADB_ROOT_PASSWORD -e 'show databases;'
+ }
diff --git a/manifests/mariadb-cluster/mariadb/values-production.yaml b/manifests/mariadb-cluster/mariadb/values-production.yaml
new file mode 100644
index 0000000..f8e3b0d
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/values-production.yaml
@@ -0,0 +1,336 @@
+## Global Docker image registry
+## Please, note that this will override the image registry for all the images, including dependencies, configured to use the global value
+##
+# global:
+# imageRegistry:
+
+## Bitnami MariaDB image
+## ref: https://hub.docker.com/r/bitnami/mariadb/tags/
+##
+image:
+ registry: docker.io
+ repository: bitnami/mariadb
+ tag: 10.1.37
+ ## Specify a imagePullPolicy
+ ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'
+ ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images
+ ##
+ pullPolicy: IfNotPresent
+ ## Optionally specify an array of imagePullSecrets.
+ ## Secrets must be manually created in the namespace.
+ ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
+ ##
+ # pullSecrets:
+ # - myRegistrKeySecretName
+
+ ## Set to true if you would like to see extra information on logs
+ ## It turns BASH and NAMI debugging in minideb
+ ## ref: https://github.com/bitnami/minideb-extras/#turn-on-bash-debugging
+ debug: false
+
+service:
+ ## Kubernetes service type, ClusterIP and NodePort are supported at present
+ type: ClusterIP
+ # clusterIp: None
+ port: 3306
+ ## Specify the nodePort value for the LoadBalancer and NodePort service types.
+ ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport
+ ##
+ # nodePort:
+ # master: 30001
+ # slave: 30002
+
+## Pods Service Account
+## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
+serviceAccount:
+ ## Specifies whether a ServiceAccount should be created
+ ##
+ create: false
+ ## The name of the ServiceAccount to use.
+ ## If not set and create is true, a name is generated using the mariadb.fullname template
+ # name:
+
+## Pod Security Context
+## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
+##
+securityContext:
+ enabled: true
+ fsGroup: 1001
+ runAsUser: 1001
+
+# # Use existing secret (ignores root, db and replication passwords)
+# existingSecret:
+
+rootUser:
+ ## MariaDB admin password
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-the-root-password-on-first-run
+ ##
+ password:
+ ##
+ ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly.
+ ## If it is not force, a random password will be generated.
+ forcePassword: true
+
+db:
+ ## MariaDB username and password
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#creating-a-database-user-on-first-run
+ ##
+ user:
+ password:
+ ## Password is ignored if existingSecret is specified.
+ ## Database to create
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#creating-a-database-on-first-run
+ ##
+ name: my_database
+ ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly.
+ ## If it is not force, a random password will be generated.
+ forcePassword: true
+
+replication:
+ ## Enable replication. This enables the creation of replicas of MariaDB. If false, only a
+ ## master deployment would be created
+ enabled: true
+ ##
+ ## MariaDB replication user
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-up-a-replication-cluster
+ ##
+ user: replicator
+ ## MariaDB replication user password
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-up-a-replication-cluster
+ ##
+ password:
+ ## Password is ignored if existingSecret is specified.
+ ##
+ ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly.
+ ## If it is not force, a random password will be generated.
+ forcePassword: true
+
+## initdb scripts
+## Specify dictionnary of scripts to be run at first boot
+## Alternatively, you can put your scripts under the files/docker-entrypoint-initdb.d directory
+##
+# initdbScripts:
+# my_init_script.sh: |
+# #!/bin/sh
+# echo "Do something."
+#
+## ConfigMap with scripts to be run at first boot
+## Note: This will override initdbScripts
+# initdbScriptsConfigMap:
+
+master:
+ ## Mariadb Master additional pod annotations
+ ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
+ # annotations:
+ # - key: key1
+ # value: value1
+
+ ## Affinity for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ ##
+ affinity: {}
+
+ ## Kept for backwards compatibility. You can now disable it by removing it.
+ ## if you wish to set it through master.affinity.podAntiAffinity instead.
+ ##
+ antiAffinity: soft
+
+ ## Tolerations for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ ##
+ tolerations: []
+
+ ## Enable persistence using Persistent Volume Claims
+ ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/
+ ##
+ persistence:
+ ## If true, use a Persistent Volume Claim, If false, use emptyDir
+ ##
+ enabled: true
+ # Enable persistence using an existing PVC
+ # existingClaim:
+ mountPath: /bitnami/mariadb
+ ## Persistent Volume Storage Class
+ ## If defined, storageClassName:
+ ## If set to "-", storageClassName: "", which disables dynamic provisioning
+ ## If undefined (the default) or set to null, no storageClassName spec is
+ ## set, choosing the default provisioner. (gp2 on AWS, standard on
+ ## GKE, AWS & OpenStack)
+ ##
+ # storageClass: "-"
+ ## Persistent Volume Claim annotations
+ ##
+ annotations: {}
+ ## Persistent Volume Access Mode
+ ##
+ accessModes:
+ - ReadWriteOnce
+ ## Persistent Volume size
+ ##
+ size: 8Gi
+ ##
+ extraInitContainers: |
+ # - name: do-something
+ # image: busybox
+ # command: ['do', 'something']
+
+ ## Configure MySQL with a custom my.cnf file
+ ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file
+ ##
+ config: |-
+ [mysqld]
+ skip-name-resolve
+ explicit_defaults_for_timestamp
+ basedir=/opt/bitnami/mariadb
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ tmpdir=/opt/bitnami/mariadb/tmp
+ max_allowed_packet=16M
+ bind-address=0.0.0.0
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+ log-error=/opt/bitnami/mariadb/logs/mysqld.log
+ character-set-server=UTF8
+ collation-server=utf8_general_ci
+
+ [client]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ default-character-set=UTF8
+
+ [manager]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+
+ ## Configure master resource requests and limits
+ ## ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ livenessProbe:
+ enabled: true
+ ##
+ ## Initializing the database could take some time
+ initialDelaySeconds: 120
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+ readinessProbe:
+ enabled: true
+ initialDelaySeconds: 15
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+
+slave:
+ replicas: 2
+
+
+ ## Mariadb Slave additional pod annotations
+ ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
+ # annotations:
+ # - key: key1
+ # value: value1
+
+ ## Affinity for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ ##
+ affinity: {}
+
+ ## Kept for backwards compatibility. You can now disable it by removing it.
+ ## if you wish to set it through slave.affinity.podAntiAffinity instead.
+ ##
+ antiAffinity: soft
+
+ ## Tolerations for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ ##
+ tolerations: []
+
+ persistence:
+ ## If true, use a Persistent Volume Claim, If false, use emptyDir
+ ##
+ enabled: true
+ # storageClass: "-"
+ annotations:
+ accessModes:
+ - ReadWriteOnce
+ ## Persistent Volume size
+ ##
+ size: 8Gi
+ ##
+ extraInitContainers: |
+ # - name: do-something
+ # image: busybox
+ # command: ['do', 'something']
+
+ ## Configure MySQL slave with a custom my.cnf file
+ ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file
+ ##
+ config: |-
+ [mysqld]
+ skip-name-resolve
+ explicit_defaults_for_timestamp
+ basedir=/opt/bitnami/mariadb
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ tmpdir=/opt/bitnami/mariadb/tmp
+ max_allowed_packet=16M
+ bind-address=0.0.0.0
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+ log-error=/opt/bitnami/mariadb/logs/mysqld.log
+ character-set-server=UTF8
+ collation-server=utf8_general_ci
+
+ [client]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ default-character-set=UTF8
+
+ [manager]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+
+ ##
+ ## Configure slave resource requests and limits
+ ## ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ livenessProbe:
+ enabled: true
+ ##
+ ## Initializing the database could take some time
+ initialDelaySeconds: 120
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+ readinessProbe:
+ enabled: true
+ initialDelaySeconds: 15
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+
+metrics:
+ enabled: true
+ image:
+ registry: docker.io
+ repository: prom/mysqld-exporter
+ tag: v0.10.0
+ pullPolicy: IfNotPresent
+ resources: {}
+ annotations:
+ prometheus.io/scrape: "true"
+ prometheus.io/port: "9104"
diff --git a/manifests/mariadb-cluster/mariadb/values.yaml b/manifests/mariadb-cluster/mariadb/values.yaml
new file mode 100644
index 0000000..9a2dfd5
--- /dev/null
+++ b/manifests/mariadb-cluster/mariadb/values.yaml
@@ -0,0 +1,335 @@
+## Global Docker image registry
+## Please, note that this will override the image registry for all the images, including dependencies, configured to use the global value
+##
+# global:
+# imageRegistry:
+
+## Bitnami MariaDB image
+## ref: https://hub.docker.com/r/bitnami/mariadb/tags/
+##
+image:
+ registry: docker.io
+ repository: bitnami/mariadb
+ tag: 10.1.37
+ ## Specify a imagePullPolicy
+ ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'
+ ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images
+ ##
+ pullPolicy: IfNotPresent
+ ## Optionally specify an array of imagePullSecrets.
+ ## Secrets must be manually created in the namespace.
+ ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
+ ##
+ # pullSecrets:
+ # - myRegistrKeySecretName
+
+ ## Set to true if you would like to see extra information on logs
+ ## It turns BASH and NAMI debugging in minideb
+ ## ref: https://github.com/bitnami/minideb-extras/#turn-on-bash-debugging
+ debug: false
+
+service:
+ ## Kubernetes service type, ClusterIP and NodePort are supported at present
+ type: ClusterIP
+ # clusterIp: None
+ port: 3306
+ ## Specify the nodePort value for the LoadBalancer and NodePort service types.
+ ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport
+ ##
+ # nodePort:
+ # master: 30001
+ # slave: 30002
+
+## Pods Service Account
+## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
+serviceAccount:
+ ## Specifies whether a ServiceAccount should be created
+ ##
+ create: false
+ ## The name of the ServiceAccount to use.
+ ## If not set and create is true, a name is generated using the mariadb.fullname template
+ # name:
+
+## Pod Security Context
+## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
+##
+securityContext:
+ enabled: true
+ fsGroup: 1001
+ runAsUser: 1001
+
+# # Use existing secret (ignores root, db and replication passwords)
+# existingSecret:
+
+rootUser:
+ ## MariaDB admin password
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-the-root-password-on-first-run
+ ##
+ password:
+ ##
+ ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly.
+ ## If it is not force, a random password will be generated.
+ forcePassword: false
+
+db:
+ ## MariaDB username and password
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#creating-a-database-user-on-first-run
+ ##
+ user:
+ password:
+ ## Password is ignored if existingSecret is specified.
+ ## Database to create
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#creating-a-database-on-first-run
+ ##
+ name: my_database
+ ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly.
+ ## If it is not force, a random password will be generated.
+ forcePassword: false
+
+replication:
+ ## Enable replication. This enables the creation of replicas of MariaDB. If false, only a
+ ## master deployment would be created
+ enabled: true
+ ##
+ ## MariaDB replication user
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-up-a-replication-cluster
+ ##
+ user: replicator
+ ## MariaDB replication user password
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-up-a-replication-cluster
+ ##
+ password:
+ ## Password is ignored if existingSecret is specified.
+ ##
+ ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly.
+ ## If it is not force, a random password will be generated.
+ forcePassword: false
+
+## initdb scripts
+## Specify dictionnary of scripts to be run at first boot
+## Alternatively, you can put your scripts under the files/docker-entrypoint-initdb.d directory
+##
+# initdbScripts:
+# my_init_script.sh: |
+# #!/bin/sh
+# echo "Do something."
+#
+## ConfigMap with scripts to be run at first boot
+## Note: This will override initdbScripts
+# initdbScriptsConfigMap:
+
+master:
+ ## Mariadb Master additional pod annotations
+ ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
+ # annotations:
+ # - key: key1
+ # value: value1
+
+ ## Affinity for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ ##
+ affinity: {}
+
+ ## Kept for backwards compatibility. You can now disable it by removing it.
+ ## if you wish to set it through master.affinity.podAntiAffinity instead.
+ ##
+ antiAffinity: soft
+
+ ## Tolerations for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ ##
+ tolerations: []
+
+ ## Enable persistence using Persistent Volume Claims
+ ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/
+ ##
+ persistence:
+ ## If true, use a Persistent Volume Claim, If false, use emptyDir
+ ##
+ enabled: true
+ # Enable persistence using an existing PVC
+ # existingClaim:
+ mountPath: /bitnami/mariadb
+ ## Persistent Volume Storage Class
+ ## If defined, storageClassName:
+ ## If set to "-", storageClassName: "", which disables dynamic provisioning
+ ## If undefined (the default) or set to null, no storageClassName spec is
+ ## set, choosing the default provisioner. (gp2 on AWS, standard on
+ ## GKE, AWS & OpenStack)
+ ##
+ # storageClass: "-"
+ ## Persistent Volume Claim annotations
+ ##
+ annotations: {}
+ ## Persistent Volume Access Mode
+ ##
+ accessModes:
+ - ReadWriteOnce
+ ## Persistent Volume size
+ ##
+ size: 8Gi
+ ##
+ extraInitContainers: |
+ # - name: do-something
+ # image: busybox
+ # command: ['do', 'something']
+
+ ## Configure MySQL with a custom my.cnf file
+ ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file
+ ##
+ config: |-
+ [mysqld]
+ skip-name-resolve
+ explicit_defaults_for_timestamp
+ basedir=/opt/bitnami/mariadb
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ tmpdir=/opt/bitnami/mariadb/tmp
+ max_allowed_packet=16M
+ bind-address=0.0.0.0
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+ log-error=/opt/bitnami/mariadb/logs/mysqld.log
+ character-set-server=UTF8
+ collation-server=utf8_general_ci
+
+ [client]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ default-character-set=UTF8
+
+ [manager]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+
+ ## Configure master resource requests and limits
+ ## ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ livenessProbe:
+ enabled: true
+ ##
+ ## Initializing the database could take some time
+ initialDelaySeconds: 120
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+ readinessProbe:
+ enabled: true
+ initialDelaySeconds: 30
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+
+slave:
+ replicas: 1
+
+ ## Mariadb Slave additional pod annotations
+ ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
+ # annotations:
+ # - key: key1
+ # value: value1
+
+ ## Affinity for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ ##
+ affinity: {}
+
+ ## Kept for backwards compatibility. You can now disable it by removing it.
+ ## if you wish to set it through slave.affinity.podAntiAffinity instead.
+ ##
+ antiAffinity: soft
+
+ ## Tolerations for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ ##
+ tolerations: []
+
+ persistence:
+ ## If true, use a Persistent Volume Claim, If false, use emptyDir
+ ##
+ enabled: true
+ # storageClass: "-"
+ annotations:
+ accessModes:
+ - ReadWriteOnce
+ ## Persistent Volume size
+ ##
+ size: 8Gi
+ ##
+ extraInitContainers: |
+ # - name: do-something
+ # image: busybox
+ # command: ['do', 'something']
+
+ ## Configure MySQL slave with a custom my.cnf file
+ ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file
+ ##
+ config: |-
+ [mysqld]
+ skip-name-resolve
+ explicit_defaults_for_timestamp
+ basedir=/opt/bitnami/mariadb
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ tmpdir=/opt/bitnami/mariadb/tmp
+ max_allowed_packet=16M
+ bind-address=0.0.0.0
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+ log-error=/opt/bitnami/mariadb/logs/mysqld.log
+ character-set-server=UTF8
+ collation-server=utf8_general_ci
+
+ [client]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ default-character-set=UTF8
+
+ [manager]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+
+ ##
+ ## Configure slave resource requests and limits
+ ## ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ livenessProbe:
+ enabled: true
+ ##
+ ## Initializing the database could take some time
+ initialDelaySeconds: 120
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+ readinessProbe:
+ enabled: true
+ initialDelaySeconds: 45
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+
+metrics:
+ enabled: false
+ image:
+ registry: docker.io
+ repository: prom/mysqld-exporter
+ tag: v0.10.0
+ pullPolicy: IfNotPresent
+ resources: {}
+ annotations:
+ prometheus.io/scrape: "true"
+ prometheus.io/port: "9104"
diff --git a/manifests/mariadb-cluster/my-values.yaml b/manifests/mariadb-cluster/my-values.yaml
new file mode 100644
index 0000000..d3370b2
--- /dev/null
+++ b/manifests/mariadb-cluster/my-values.yaml
@@ -0,0 +1,368 @@
+## Global Docker image registry
+## Please, note that this will override the image registry for all the images, including dependencies, configured to use the global value
+##
+# global:
+# imageRegistry:
+
+## Bitnami MariaDB image
+## ref: https://hub.docker.com/r/bitnami/mariadb/tags/
+##
+image:
+ registry: docker.io
+ repository: bitnami/mariadb
+ tag: 10.1.37
+ ## Specify a imagePullPolicy
+ ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'
+ ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images
+ ##
+ pullPolicy: IfNotPresent
+ ## Optionally specify an array of imagePullSecrets.
+ ## Secrets must be manually created in the namespace.
+ ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
+ ##
+ # pullSecrets:
+ # - myRegistrKeySecretName
+
+ ## Set to true if you would like to see extra information on logs
+ ## It turns BASH and NAMI debugging in minideb
+ ## ref: https://github.com/bitnami/minideb-extras/#turn-on-bash-debugging
+ debug: false
+
+service:
+ ## Kubernetes service type, ClusterIP and NodePort are supported at present
+ type: NodePort
+ # clusterIp: None
+ port: 3306
+ ## Specify the nodePort value for the LoadBalancer and NodePort service types.
+ ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport
+ ##
+ nodePort:
+ master: 33306
+ slave: 33307
+
+## Pods Service Account
+## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
+serviceAccount:
+ ## Specifies whether a ServiceAccount should be created
+ ##
+ create: false
+ ## The name of the ServiceAccount to use.
+ ## If not set and create is true, a name is generated using the mariadb.fullname template
+ # name:
+
+## Pod Security Context
+## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
+##
+securityContext:
+ enabled: true
+ fsGroup: 1001
+ runAsUser: 1001
+
+# # Use existing secret (ignores root, db and replication passwords)
+# existingSecret:
+
+rootUser:
+ ## MariaDB admin password
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-the-root-password-on-first-run
+ ##
+ password: test.c0m
+ ##
+ ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly.
+ ## If it is not force, a random password will be generated.
+ forcePassword: true
+
+db:
+ ## MariaDB username and password
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#creating-a-database-user-on-first-run
+ ##
+ user: hello
+ password: hello
+ ## Password is ignored if existingSecret is specified.
+ ## Database to create
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#creating-a-database-on-first-run
+ ##
+ name: hello
+ ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly.
+ ## If it is not force, a random password will be generated.
+ forcePassword: true
+
+replication:
+ ## Enable replication. This enables the creation of replicas of MariaDB. If false, only a
+ ## master deployment would be created
+ enabled: true
+ ##
+ ## MariaDB replication user
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-up-a-replication-cluster
+ ##
+ user: replicator
+ ## MariaDB replication user password
+ ## ref: https://github.com/bitnami/bitnami-docker-mariadb#setting-up-a-replication-cluster
+ ##
+ password: R4%forep11CAT0r
+ ## Password is ignored if existingSecret is specified.
+ ##
+ ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly.
+ ## If it is not force, a random password will be generated.
+ forcePassword: true
+
+## initdb scripts
+## Specify dictionnary of scripts to be run at first boot
+## Alternatively, you can put your scripts under the files/docker-entrypoint-initdb.d directory
+##
+# initdbScripts:
+# my_init_script.sh: |
+# #!/bin/sh
+# echo "Do something."
+#
+## ConfigMap with scripts to be run at first boot
+## Note: This will override initdbScripts
+# initdbScriptsConfigMap:
+
+master:
+ ## Mariadb Master additional pod annotations
+ ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
+ # annotations:
+ # - key: key1
+ # value: value1
+
+ ## Affinity for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ ##
+ affinity: {}
+
+ ## Kept for backwards compatibility. You can now disable it by removing it.
+ ## if you wish to set it through master.affinity.podAntiAffinity instead.
+ ##
+ antiAffinity: soft
+
+ ## Tolerations for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ ##
+ tolerations: []
+
+ ## Enable persistence using Persistent Volume Claims
+ ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/
+ ##
+ persistence:
+ ## If true, use a Persistent Volume Claim, If false, use emptyDir
+ ##
+ enabled: true
+ # Enable persistence using an existing PVC
+ # existingClaim:
+ mountPath: /bitnami/mariadb
+ ## Persistent Volume Storage Class
+ ## If defined, storageClassName:
+ ## If set to "-", storageClassName: "", which disables dynamic provisioning
+ ## If undefined (the default) or set to null, no storageClassName spec is
+ ## set, choosing the default provisioner. (gp2 on AWS, standard on
+ ## GKE, AWS & OpenStack)
+ ##
+ storageClass: "nfs-db"
+ ## Persistent Volume Claim annotations
+ ##
+ annotations: {}
+ ## Persistent Volume Access Mode
+ ##
+ accessModes:
+ - ReadWriteOnce
+ ## Persistent Volume size
+ ##
+ size: 5Gi
+ ##
+ extraInitContainers: |
+ # - name: do-something
+ # image: busybox
+ # command: ['do', 'something']
+
+ ## Configure MySQL with a custom my.cnf file
+ ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file
+ ##
+ config: |-
+ [mysqld]
+ skip-name-resolve
+ explicit_defaults_for_timestamp
+ basedir=/opt/bitnami/mariadb
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ tmpdir=/opt/bitnami/mariadb/tmp
+ bind-address=0.0.0.0
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+ log-error=/opt/bitnami/mariadb/logs/mysqld.log
+ character-set-server=UTF8
+ collation-server=utf8_general_ci
+ # optimize
+ max_allowed_packet = 1024M
+ table_open_cache = 512
+ sort_buffer_size = 2M
+ read_buffer_size = 2M
+ read_rnd_buffer_size = 8M
+ thread_cache_size = 8
+ query_cache_size = 32M
+ max_heap_table_size=1024M
+ tmp_table_size=1024M
+ max_connections=65535
+ max_connect_errors=65535
+ wait_timeout=172800
+ interactive_timeout=172800
+ connect_timeout=30
+ # log settings
+ expire_logs_days=3
+
+ [client]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ default-character-set=UTF8
+
+ [manager]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+
+ ## Configure master resource requests and limits
+ ## ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ livenessProbe:
+ enabled: true
+ ##
+ ## Initializing the database could take some time
+ initialDelaySeconds: 120
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+ readinessProbe:
+ enabled: true
+ initialDelaySeconds: 15
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+
+slave:
+ replicas: 1
+
+
+ ## Mariadb Slave additional pod annotations
+ ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
+ # annotations:
+ # - key: key1
+ # value: value1
+
+ ## Affinity for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+ ##
+ affinity: {}
+
+ ## Kept for backwards compatibility. You can now disable it by removing it.
+ ## if you wish to set it through slave.affinity.podAntiAffinity instead.
+ ##
+ antiAffinity: soft
+
+ ## Tolerations for pod assignment
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+ ##
+ tolerations: []
+
+ persistence:
+ ## If true, use a Persistent Volume Claim, If false, use emptyDir
+ ##
+ enabled: false
+ # storageClass: "-"
+ annotations:
+ accessModes:
+ - ReadWriteOnce
+ ## Persistent Volume size
+ ##
+ size: 5Gi
+ ##
+ extraInitContainers: |
+ # - name: do-something
+ # image: busybox
+ # command: ['do', 'something']
+
+ ## Configure MySQL slave with a custom my.cnf file
+ ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file
+ ##
+ config: |-
+ [mysqld]
+ skip-name-resolve
+ explicit_defaults_for_timestamp
+ basedir=/opt/bitnami/mariadb
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ tmpdir=/opt/bitnami/mariadb/tmp
+ bind-address=0.0.0.0
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+ log-error=/opt/bitnami/mariadb/logs/mysqld.log
+ character-set-server=UTF8
+ collation-server=utf8_general_ci
+ # optimize
+ max_allowed_packet = 1024M
+ table_open_cache = 512
+ sort_buffer_size = 2M
+ read_buffer_size = 2M
+ read_rnd_buffer_size = 8M
+ thread_cache_size = 8
+ query_cache_size = 32M
+ max_heap_table_size=1024M
+ tmp_table_size=1024M
+ max_connections=65535
+ max_connect_errors=65535
+ wait_timeout=172800
+ interactive_timeout=172800
+ connect_timeout=30
+ # log settings
+ expire_logs_days=3
+
+ [client]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ default-character-set=UTF8
+
+ [manager]
+ port=3306
+ socket=/opt/bitnami/mariadb/tmp/mysql.sock
+ pid-file=/opt/bitnami/mariadb/tmp/mysqld.pid
+
+ ##
+ ## Configure slave resource requests and limits
+ ## ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ livenessProbe:
+ enabled: true
+ ##
+ ## Initializing the database could take some time
+ initialDelaySeconds: 120
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+ readinessProbe:
+ enabled: true
+ initialDelaySeconds: 15
+ ##
+ ## Default Kubernetes values
+ periodSeconds: 10
+ timeoutSeconds: 1
+ successThreshold: 1
+ failureThreshold: 3
+
+metrics:
+ enabled: false
+ image:
+ registry: docker.io
+ repository: prom/mysqld-exporter
+ tag: v0.10.0
+ pullPolicy: IfNotPresent
+ resources: {}
+ annotations:
+ prometheus.io/scrape: "true"
+ prometheus.io/port: "9104"
diff --git a/manifests/metrics-server/auth-delegator.yaml b/manifests/metrics-server/auth-delegator.yaml
new file mode 100644
index 0000000..e3442c5
--- /dev/null
+++ b/manifests/metrics-server/auth-delegator.yaml
@@ -0,0 +1,13 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ name: metrics-server:system:auth-delegator
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: system:auth-delegator
+subjects:
+- kind: ServiceAccount
+ name: metrics-server
+ namespace: kube-system
diff --git a/manifests/metrics-server/auth-reader.yaml b/manifests/metrics-server/auth-reader.yaml
new file mode 100644
index 0000000..f0616e1
--- /dev/null
+++ b/manifests/metrics-server/auth-reader.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: RoleBinding
+metadata:
+ name: metrics-server-auth-reader
+ namespace: kube-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: extension-apiserver-authentication-reader
+subjects:
+- kind: ServiceAccount
+ name: metrics-server
+ namespace: kube-system
diff --git a/manifests/metrics-server/metrics-apiservice.yaml b/manifests/metrics-server/metrics-apiservice.yaml
new file mode 100644
index 0000000..08b0530
--- /dev/null
+++ b/manifests/metrics-server/metrics-apiservice.yaml
@@ -0,0 +1,14 @@
+---
+apiVersion: apiregistration.k8s.io/v1beta1
+kind: APIService
+metadata:
+ name: v1beta1.metrics.k8s.io
+spec:
+ service:
+ name: metrics-server
+ namespace: kube-system
+ group: metrics.k8s.io
+ version: v1beta1
+ insecureSkipTLSVerify: true
+ groupPriorityMinimum: 100
+ versionPriority: 100
diff --git a/manifests/metrics-server/metrics-server-deployment.yaml b/manifests/metrics-server/metrics-server-deployment.yaml
new file mode 100644
index 0000000..e2bfa67
--- /dev/null
+++ b/manifests/metrics-server/metrics-server-deployment.yaml
@@ -0,0 +1,41 @@
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: metrics-server
+ namespace: kube-system
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: metrics-server
+ namespace: kube-system
+ labels:
+ k8s-app: metrics-server
+spec:
+ selector:
+ matchLabels:
+ k8s-app: metrics-server
+ template:
+ metadata:
+ name: metrics-server
+ labels:
+ k8s-app: metrics-server
+ spec:
+ serviceAccountName: metrics-server
+ volumes:
+ # mount in tmp so we can safely use from-scratch images and/or read-only containers
+ - name: tmp-dir
+ emptyDir: {}
+ containers:
+ - name: metrics-server
+ #image: k8s.gcr.io/metrics-server-amd64:v0.3.0
+ image: mirrorgooglecontainers/metrics-server-amd64:v0.3.1
+ imagePullPolicy: IfNotPresent
+ command:
+ - /metrics-server
+ - --metric-resolution=30s
+ - --kubelet-insecure-tls
+ volumeMounts:
+ - name: tmp-dir
+ mountPath: /tmp
diff --git a/manifests/metrics-server/metrics-server-service.yaml b/manifests/metrics-server/metrics-server-service.yaml
new file mode 100644
index 0000000..082b00c
--- /dev/null
+++ b/manifests/metrics-server/metrics-server-service.yaml
@@ -0,0 +1,15 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: metrics-server
+ namespace: kube-system
+ labels:
+ kubernetes.io/name: "Metrics-server"
+spec:
+ selector:
+ k8s-app: metrics-server
+ ports:
+ - port: 443
+ protocol: TCP
+ targetPort: 443
diff --git a/manifests/metrics-server/resource-reader.yaml b/manifests/metrics-server/resource-reader.yaml
new file mode 100644
index 0000000..34294a3
--- /dev/null
+++ b/manifests/metrics-server/resource-reader.yaml
@@ -0,0 +1,38 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: system:metrics-server
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - pods
+ - nodes
+ - nodes/stats
+ - namespaces
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - "extensions"
+ resources:
+ - deployments
+ verbs:
+ - get
+ - list
+ - watch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: system:metrics-server
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: system:metrics-server
+subjects:
+- kind: ServiceAccount
+ name: metrics-server
+ namespace: kube-system
diff --git a/manifests/mysql-cluster/mysql-configmap.yaml b/manifests/mysql-cluster/mysql-configmap.yaml
new file mode 100644
index 0000000..46d34e4
--- /dev/null
+++ b/manifests/mysql-cluster/mysql-configmap.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: mysql
+ labels:
+ app: mysql
+data:
+ master.cnf: |
+ # Apply this config only on the master.
+ [mysqld]
+ log-bin
+ slave.cnf: |
+ # Apply this config only on slaves.
+ [mysqld]
+ super-read-only
+
diff --git a/manifests/mysql-cluster/mysql-services.yaml b/manifests/mysql-cluster/mysql-services.yaml
new file mode 100644
index 0000000..f538992
--- /dev/null
+++ b/manifests/mysql-cluster/mysql-services.yaml
@@ -0,0 +1,30 @@
+# Headless service for stable DNS entries of StatefulSet members.
+apiVersion: v1
+kind: Service
+metadata:
+ name: mysql
+ labels:
+ app: mysql
+spec:
+ ports:
+ - name: mysql
+ port: 3306
+ clusterIP: None
+ selector:
+ app: mysql
+---
+# Client service for connecting to any MySQL instance for reads.
+# For writes, you must instead connect to the master: mysql-0.mysql.
+apiVersion: v1
+kind: Service
+metadata:
+ name: mysql-read
+ labels:
+ app: mysql
+spec:
+ ports:
+ - name: mysql
+ port: 3306
+ selector:
+ app: mysql
+
diff --git a/manifests/mysql-cluster/mysql-statefulset.yaml b/manifests/mysql-cluster/mysql-statefulset.yaml
new file mode 100644
index 0000000..d7d98a8
--- /dev/null
+++ b/manifests/mysql-cluster/mysql-statefulset.yaml
@@ -0,0 +1,171 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: mysql
+spec:
+ selector:
+ matchLabels:
+ app: mysql
+ serviceName: mysql
+ replicas: 2
+ template:
+ metadata:
+ labels:
+ app: mysql
+ spec:
+ initContainers:
+ - name: init-mysql
+ image: mysql:5.7
+ command:
+ - bash
+ - "-c"
+ - |
+ set -ex
+ # Generate mysql server-id from pod ordinal index.
+ [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
+ ordinal=${BASH_REMATCH[1]}
+ echo [mysqld] > /mnt/conf.d/server-id.cnf
+ # Add an offset to avoid reserved server-id=0 value.
+ echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
+ # Copy appropriate conf.d files from config-map to emptyDir.
+ if [[ $ordinal -eq 0 ]]; then
+ cp /mnt/config-map/master.cnf /mnt/conf.d/
+ else
+ cp /mnt/config-map/slave.cnf /mnt/conf.d/
+ fi
+ volumeMounts:
+ - name: conf
+ mountPath: /mnt/conf.d
+ - name: config-map
+ mountPath: /mnt/config-map
+ - name: clone-mysql
+ #image: gcr.io/google-samples/xtrabackup:1.0
+ image: jmgao1983/xtrabackup:1.0
+ command:
+ - bash
+ - "-c"
+ - |
+ set -ex
+ # Skip the clone if data already exists.
+ [[ -d /var/lib/mysql/mysql ]] && exit 0
+ # Skip the clone on master (ordinal index 0).
+ [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
+ ordinal=${BASH_REMATCH[1]}
+ [[ $ordinal -eq 0 ]] && exit 0
+ # Clone data from previous peer.
+ ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
+ # Prepare the backup.
+ xtrabackup --prepare --target-dir=/var/lib/mysql
+ volumeMounts:
+ - name: data
+ mountPath: /var/lib/mysql
+ subPath: mysql
+ - name: conf
+ mountPath: /etc/mysql/conf.d
+ containers:
+ - name: mysql
+ image: mysql:5.7
+ env:
+ - name: MYSQL_ALLOW_EMPTY_PASSWORD
+ value: "1"
+ ports:
+ - name: mysql
+ containerPort: 3306
+ hostPort: 3306
+ volumeMounts:
+ - name: data
+ mountPath: /var/lib/mysql
+ subPath: mysql
+ - name: conf
+ mountPath: /etc/mysql/conf.d
+ resources:
+ requests:
+ cpu: 500m
+ memory: 1Gi
+ livenessProbe:
+ exec:
+ command: ["mysqladmin", "ping"]
+ initialDelaySeconds: 30
+ periodSeconds: 10
+ timeoutSeconds: 5
+ readinessProbe:
+ exec:
+ # Check we can execute queries over TCP (skip-networking is off).
+ command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
+ initialDelaySeconds: 5
+ periodSeconds: 2
+ timeoutSeconds: 1
+ - name: xtrabackup
+ #image: gcr.io/google-samples/xtrabackup:1.0
+ image: jmgao1983/xtrabackup:1.0
+ ports:
+ - name: xtrabackup
+ containerPort: 3307
+ command:
+ - bash
+ - "-c"
+ - |
+ set -ex
+ cd /var/lib/mysql
+
+ # Determine binlog position of cloned data, if any.
+ if [[ -f xtrabackup_slave_info ]]; then
+ # XtraBackup already generated a partial "CHANGE MASTER TO" query
+ # because we're cloning from an existing slave.
+ mv xtrabackup_slave_info change_master_to.sql.in
+ # Ignore xtrabackup_binlog_info in this case (it's useless).
+ rm -f xtrabackup_binlog_info
+ elif [[ -f xtrabackup_binlog_info ]]; then
+ # We're cloning directly from master. Parse binlog position.
+ [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
+ rm xtrabackup_binlog_info
+ echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
+ MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
+ fi
+
+ # Check if we need to complete a clone by starting replication.
+ if [[ -f change_master_to.sql.in ]]; then
+ echo "Waiting for mysqld to be ready (accepting connections)"
+ until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done
+
+ echo "Initializing replication from clone position"
+ # In case of container restart, attempt this at-most-once.
+ mv change_master_to.sql.in change_master_to.sql.orig
+ mysql -h 127.0.0.1 < 5)",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "",
+ "refId": "A",
+ "step": 600
+ }
+ ],
+ "thresholds": "1, 3",
+ "title": "Crashlooping Pods",
+ "transparent": false,
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "0",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": true,
+ "colors": [
+ "rgba(50, 172, 45, 0.97)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(245, 54, 54, 0.9)"
+ ],
+ "datasource": "MYDS_Prometheus",
+ "editable": true,
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 3,
+ "x": 6,
+ "y": 3
+ },
+ "hideTimeOverride": false,
+ "id": 5,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": false
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "sum(kube_node_status_condition{condition=\"Ready\",status!=\"true\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "",
+ "refId": "A",
+ "step": 600
+ }
+ ],
+ "thresholds": "1, 3",
+ "title": "Node Not Ready",
+ "transparent": false,
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "N/A",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": true,
+ "colors": [
+ "rgba(50, 172, 45, 0.97)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(245, 54, 54, 0.9)"
+ ],
+ "datasource": "MYDS_Prometheus",
+ "editable": true,
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 3,
+ "x": 9,
+ "y": 3
+ },
+ "hideTimeOverride": false,
+ "id": 2,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": false
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "sum(ALERTS{alertstate=\"firing\",alertname!=\"DeadMansSwitch\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "",
+ "refId": "A",
+ "step": 600
+ }
+ ],
+ "thresholds": "1, 3",
+ "title": "Alerts Firing",
+ "transparent": false,
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "0",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": false,
+ "colorValue": true,
+ "colors": [
+ "rgba(50, 172, 45, 0.97)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(245, 54, 54, 0.9)"
+ ],
+ "datasource": "MYDS_Prometheus",
+ "editable": true,
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 3,
+ "x": 12,
+ "y": 3
+ },
+ "hideTimeOverride": false,
+ "id": 3,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": false
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "sum(ALERTS{alertstate=\"pending\",alertname!=\"DeadMansSwitch\"})",
+ "format": "time_series",
+ "intervalFactor": 2,
+ "legendFormat": "",
+ "refId": "A",
+ "step": 600
+ }
+ ],
+ "thresholds": "3, 5",
+ "title": "Alerts Pending",
+ "transparent": false,
+ "type": "singlestat",
+ "valueFontSize": "80%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "0",
+ "value": "null"
+ }
+ ],
+ "valueName": "current"
+ }
+ ],
+ "refresh": "10s",
+ "schemaVersion": 16,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-12h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "browser",
+ "title": "Kubernetes Cluster Health (Prometheus)",
+ "uid": "NEYiLUzik",
+ "version": 1
+ }
diff --git a/manifests/prometheus/grafana-settings.yaml b/manifests/prometheus/grafana-settings.yaml
new file mode 100644
index 0000000..cd4eef1
--- /dev/null
+++ b/manifests/prometheus/grafana-settings.yaml
@@ -0,0 +1,32 @@
+service:
+ type: NodePort
+ nodePort: 39002
+
+adminUser: admin
+adminPassword: admin
+
+datasources:
+ datasources.yaml:
+ apiVersion: 1
+ datasources:
+ - name: MYDS_Prometheus
+ type: prometheus
+ #url: http:// + 集群里prometheus-server的服务名
+ #可以用 kubectl get svc --all-namespaces |grep prometheus-server查看
+ url: http://monitor-prometheus-server
+ access: proxy
+ isDefault: true
+
+dashboardProviders:
+ dashboardproviders.yaml:
+ apiVersion: 1
+ providers:
+ - name: 'default'
+ orgId: 1
+ folder: ''
+ type: file
+ disableDeletion: false
+ editable: true
+ options:
+ path: /var/lib/grafana/dashboards
+
diff --git a/manifests/prometheus/grafana/Chart.yaml b/manifests/prometheus/grafana/Chart.yaml
new file mode 100755
index 0000000..3249599
--- /dev/null
+++ b/manifests/prometheus/grafana/Chart.yaml
@@ -0,0 +1,15 @@
+name: grafana
+version: 1.16.0
+appVersion: 5.2.4
+kubeVersion: "^1.8.0-0"
+description: The leading tool for querying and visualizing time series and metrics.
+home: https://grafana.net
+icon: https://raw.githubusercontent.com/grafana/grafana/master/public/img/logo_transparent_400x.png
+sources:
+ - https://github.com/grafana/grafana
+maintainers:
+ - name: zanhsieh
+ email: zanhsieh@gmail.com
+ - name: rtluckie
+ email: rluckie@cisco.com
+engine: gotpl
diff --git a/manifests/prometheus/grafana/README.md b/manifests/prometheus/grafana/README.md
new file mode 100644
index 0000000..8c4cc7a
--- /dev/null
+++ b/manifests/prometheus/grafana/README.md
@@ -0,0 +1,162 @@
+# Grafana Helm Chart
+
+* Installs the web dashboarding system [Grafana](http://grafana.org/)
+
+## TL;DR;
+
+```console
+$ helm install stable/grafana
+```
+
+## Installing the Chart
+
+To install the chart with the release name `my-release`:
+
+```console
+$ helm install --name my-release stable/grafana
+```
+
+## Uninstalling the Chart
+
+To uninstall/delete the my-release deployment:
+
+```console
+$ helm delete my-release
+```
+
+The command removes all the Kubernetes components associated with the chart and deletes the release.
+
+
+## Configuration
+
+| Parameter | Description | Default |
+|---------------------------------|-----------------------------------------------|---------------------------------------------------------|
+| `replicas` | Number of nodes | `1` |
+| `deploymentStrategy` | Deployment strategy | `RollingUpdate` |
+| `livenessProbe` | Liveness Probe settings | `{ "httpGet": { "path": "/api/health", "port": 3000 } }` |
+| `readinessProbe` | Rediness Probe settings | `{ "httpGet": { "path": "/api/health", "port": 3000 } "initialDelaySeconds": 60, "timeoutSeconds": 30, "failureThreshold": 10, "periodSeconds": 10 }` |
+| `securityContext` | Deployment securityContext | `{"runAsUser": 472, "fsGroup": 472}` |
+| `image.repository` | Image repository | `grafana/grafana` |
+| `image.tag` | Image tag. (`Must be >= 5.0.0`) | `5.2.4` |
+| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
+| `service.type` | Kubernetes service type | `ClusterIP` |
+| `service.port` | Kubernetes port where service is exposed | `80` |
+| `service.annotations` | Service annotations | `{}` |
+| `service.labels` | Custom labels | `{}` |
+| `ingress.enabled` | Enables Ingress | `false` |
+| `ingress.annotations` | Ingress annotations | `{}` |
+| `ingress.labels` | Custom labels | `{}` |
+| `ingress.hosts` | Ingress accepted hostnames | `[]` |
+| `ingress.tls` | Ingress TLS configuration | `[]` |
+| `resources` | CPU/Memory resource requests/limits | `{}` |
+| `nodeSelector` | Node labels for pod assignment | `{}` |
+| `tolerations` | Toleration labels for pod assignment | `[]` |
+| `affinity` | Affinity settings for pod assignment | `{}` |
+| `persistence.enabled` | Use persistent volume to store data | `false` |
+| `persistence.size` | Size of persistent volume claim | `10Gi` |
+| `persistence.existingClaim` | Use an existing PVC to persist data | `nil` |
+| `persistence.storageClassName` | Type of persistent volume claim | `nil` |
+| `persistence.accessModes` | Persistence access modes | `[]` |
+| `persistence.subPath` | Mount a sub dir of the persistent volume | `""` |
+| `schedulerName` | Alternate scheduler name | `nil` |
+| `env` | Extra environment variables passed to pods | `{}` |
+| `envFromSecret` | Name of a Kubenretes secret (must be manually created in the same namespace) containing values to be added to the environment | `""` |
+| `extraSecretMounts` | Additional grafana server secret mounts | `[]` |
+| `plugins` | Plugins to be loaded along with Grafana | `[]` |
+| `datasources` | Configure grafana datasources | `{}` |
+| `dashboardProviders` | Configure grafana dashboard providers | `{}` |
+| `dashboards` | Dashboards to import | `{}` |
+| `dashboardsConfigMaps` | ConfigMaps reference that contains dashboards | `{}` |
+| `grafana.ini` | Grafana's primary configuration | `{}` |
+| `ldap.existingSecret` | The name of an existing secret containing the `ldap.toml` file, this must have the key `ldap-toml`. | `""` |
+| `ldap.config ` | Grafana's LDAP configuration | `""` |
+| `annotations` | Deployment annotations | `{}` |
+| `podAnnotations` | Pod annotations | `{}` |
+| `sidecar.dashboards.enabled` | Enabled the cluster wide search for dashboards and adds/updates/deletes them in grafana | `false` |
+| `sidecar.dashboards.label` | Label that config maps with dashboards should have to be added | `false` |
+| `sidecar.datasources.enabled` | Enabled the cluster wide search for datasources and adds/updates/deletes them in grafana |`false` |
+| `sidecar.datasources.label` | Label that config maps with datasources should have to be added | `false` |
+| `smtp.existingSecret` | The name of an existing secret containing the SMTP credentials, this must have the keys `user` and `password`. | `""` |
+
+## Sidecar for dashboards
+
+If the parameter `sidecar.dashboards.enabled` is set, a sidecar container is deployed in the grafana pod. This container watches all config maps in the cluster and filters out the ones with a label as defined in `sidecar.dashboards.label`. The files defined in those configmaps are written to a folder and accessed by grafana. Changes to the configmaps are monitored and the imported dashboards are deleted/updated. A recommendation is to use one configmap per dashboard, as an reduction of multiple dashboards inside one configmap is currently not properly mirrored in grafana.
+Example dashboard config:
+```
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: sample-grafana-dashboard
+ labels:
+ grafana_dashboard: 1
+data:
+ k8s-dashboard.json: |-
+ [...]
+```
+
+## Sidecar for datasources
+
+If the parameter `sidecar.datasource.enabled` is set, a sidecar container is deployed in the grafana pod. This container watches all config maps in the cluster and filters out the ones with a label as defined in `sidecar.datasources.label`. The files defined in those configmaps are written to a folder and accessed by grafana on startup. Using these yaml files, the data sources in grafana can be modified.
+
+Example datasource config adapted from [Grafana](http://docs.grafana.org/administration/provisioning/#example-datasource-config-file):
+```
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: sample-grafana-datasource
+ labels:
+ grafana_datasource: 1
+data:
+ datasource.yaml: |-
+ # config file version
+ apiVersion: 1
+
+ # list of datasources that should be deleted from the database
+ deleteDatasources:
+ - name: Graphite
+ orgId: 1
+
+ # list of datasources to insert/update depending
+ # whats available in the database
+ datasources:
+ # name of the datasource. Required
+ - name: Graphite
+ # datasource type. Required
+ type: graphite
+ # access mode. proxy or direct (Server or Browser in the UI). Required
+ access: proxy
+ # org id. will default to orgId 1 if not specified
+ orgId: 1
+ # url
+ url: http://localhost:8080
+ # database password, if used
+ password:
+ # database user, if used
+ user:
+ # database name, if used
+ database:
+ # enable/disable basic auth
+ basicAuth:
+ # basic auth username
+ basicAuthUser:
+ # basic auth password
+ basicAuthPassword:
+ # enable/disable with credentials headers
+ withCredentials:
+ # mark as default datasource. Max one per org
+ isDefault:
+ # fields that will be converted to json and stored in json_data
+ jsonData:
+ graphiteVersion: "1.1"
+ tlsAuth: true
+ tlsAuthWithCACert: true
+ # json object of data that will be encrypted.
+ secureJsonData:
+ tlsCACert: "..."
+ tlsClientCert: "..."
+ tlsClientKey: "..."
+ version: 1
+ # allow users to edit datasources from the UI.
+ editable: false
+
+```
diff --git a/manifests/prometheus/grafana/templates/NOTES.txt b/manifests/prometheus/grafana/templates/NOTES.txt
new file mode 100644
index 0000000..634e30a
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/NOTES.txt
@@ -0,0 +1,37 @@
+1. Get your '{{ .Values.adminUser }}' user password by running:
+
+ kubectl get secret --namespace {{ .Release.Namespace }} {{ template "grafana.fullname" . }} -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
+
+2. The Grafana server can be accessed via port {{ .Values.service.port }} on the following DNS name from within your cluster:
+
+ {{ template "grafana.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
+{{ if .Values.ingress.enabled }}
+ From outside the cluster, the server URL(s) are:
+{{- range .Values.ingress.hosts }}
+ http://{{ . }}
+{{- end }}
+{{ else }}
+ Get the Grafana URL to visit by running these commands in the same shell:
+{{ if contains "NodePort" .Values.service.type -}}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "grafana.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+{{ else if contains "LoadBalancer" .Values.service.type -}}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "grafana.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "grafana.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ http://$SERVICE_IP:{{ .Values.service.port -}}
+{{ else if contains "ClusterIP" .Values.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "grafana.fullname" . }},component={{ .Values.name }}" -o jsonpath="{.items[0].metadata.name}")
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 3000
+{{- end }}
+{{- end }}
+
+3. Login with the password from step 1 and the username: {{ .Values.adminUser }}
+
+{{- if not .Values.persistence.enabled }}
+#################################################################################
+###### WARNING: Persistence is disabled!!! You will lose your data when #####
+###### the Grafana pod is terminated. #####
+#################################################################################
+{{- end }}
diff --git a/manifests/prometheus/grafana/templates/_helpers.tpl b/manifests/prometheus/grafana/templates/_helpers.tpl
new file mode 100644
index 0000000..3a3ebd3
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/_helpers.tpl
@@ -0,0 +1,43 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "grafana.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "grafana.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "grafana.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create the name of the service account
+*/}}
+{{- define "grafana.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create -}}
+ {{ default (include "grafana.fullname" .) .Values.serviceAccount.name }}
+{{- else -}}
+ {{ default "default" .Values.serviceAccount.name }}
+{{- end -}}
+{{- end -}}
diff --git a/manifests/prometheus/grafana/templates/clusterrole.yaml b/manifests/prometheus/grafana/templates/clusterrole.yaml
new file mode 100644
index 0000000..47452f4
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/clusterrole.yaml
@@ -0,0 +1,23 @@
+{{- if .Values.rbac.create }}
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ template "grafana.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+{{- with .Values.annotations }}
+ annotations:
+{{ toYaml . | indent 4 }}
+{{- end }}
+ name: {{ template "grafana.fullname" . }}-clusterrole
+{{- if or .Values.sidecar.dashboards.enabled .Values.sidecar.datasources.enabled }}
+rules:
+- apiGroups: [""] # "" indicates the core API group
+ resources: ["configmaps"]
+ verbs: ["get", "watch", "list"]
+{{- else }}
+rules: []
+{{- end}}
+{{- end}}
diff --git a/manifests/prometheus/grafana/templates/clusterrolebinding.yaml b/manifests/prometheus/grafana/templates/clusterrolebinding.yaml
new file mode 100644
index 0000000..093e551
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/clusterrolebinding.yaml
@@ -0,0 +1,23 @@
+{{- if .Values.rbac.create }}
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: {{ template "grafana.fullname" . }}-clusterrolebinding
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ template "grafana.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+{{- with .Values.annotations }}
+ annotations:
+{{ toYaml . | indent 4 }}
+{{- end }}
+subjects:
+ - kind: ServiceAccount
+ name: {{ template "grafana.serviceAccountName" . }}
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ kind: ClusterRole
+ name: {{ template "grafana.fullname" . }}-clusterrole
+ apiGroup: rbac.authorization.k8s.io
+{{- end}}
diff --git a/manifests/prometheus/grafana/templates/configmap-dashboard-provider.yaml b/manifests/prometheus/grafana/templates/configmap-dashboard-provider.yaml
new file mode 100644
index 0000000..0771731
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/configmap-dashboard-provider.yaml
@@ -0,0 +1,26 @@
+{{- if .Values.sidecar.dashboards.enabled }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ template "grafana.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+{{- with .Values.annotations }}
+ annotations:
+{{ toYaml . | indent 4 }}
+{{- end }}
+ name: {{ template "grafana.fullname" . }}-config-dashboards
+data:
+ provider.yaml: |-
+ apiVersion: 1
+ providers:
+ - name: 'default'
+ orgId: 1
+ folder: ''
+ type: file
+ disableDeletion: false
+ options:
+ path: {{ .Values.sidecar.dashboards.folder }}
+{{- end}}
diff --git a/manifests/prometheus/grafana/templates/configmap.yaml b/manifests/prometheus/grafana/templates/configmap.yaml
new file mode 100644
index 0000000..a499e1f
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/configmap.yaml
@@ -0,0 +1,61 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "grafana.fullname" . }}
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ template "grafana.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+data:
+{{- if .Values.plugins }}
+ plugins: {{ join "," .Values.plugins }}
+{{- end }}
+ grafana.ini: |
+{{- range $key, $value := index .Values "grafana.ini" }}
+ [{{ $key }}]
+ {{- range $elem, $elemVal := $value }}
+ {{ $elem }} = {{ $elemVal }}
+ {{- end }}
+{{- end }}
+
+{{- if .Values.datasources }}
+ {{- range $key, $value := .Values.datasources }}
+ {{ $key }}: |
+{{ toYaml $value | indent 4 }}
+ {{- end -}}
+{{- end -}}
+
+{{- if .Values.dashboardProviders }}
+ {{- range $key, $value := .Values.dashboardProviders }}
+ {{ $key }}: |
+{{ toYaml $value | indent 4 }}
+ {{- end -}}
+{{- end -}}
+
+{{- if .Values.dashboards }}
+ download_dashboards.sh: |
+ #!/usr/bin/env sh
+ set -euf
+ {{- if .Values.dashboardProviders }}
+ {{- range $key, $value := .Values.dashboardProviders }}
+ {{- range $value.providers }}
+ mkdir -p {{ .options.path }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+
+ {{- range $provider, $dashboards := .Values.dashboards }}
+ {{- range $key, $value := $dashboards }}
+ {{- if (or (hasKey $value "gnetId") (hasKey $value "url")) }}
+ curl -sk \
+ --connect-timeout 60 \
+ --max-time 60 \
+ -H "Accept: application/json" \
+ -H "Content-Type: application/json;charset=UTF-8" \
+ {{- if $value.url -}}{{ $value.url }}{{- else -}} https://grafana.com/api/dashboards/{{ $value.gnetId }}/revisions/{{- if $value.revision -}}{{ $value.revision }}{{- else -}}1{{- end -}}/download{{- end -}}{{ if $value.datasource }}| sed 's|\"datasource\":[^,]*|\"datasource\": \"{{ $value.datasource }}\"|g'{{ end }} \
+ > /var/lib/grafana/dashboards/{{ $provider }}/{{ $key }}.json
+ {{- end }}
+ {{- end }}
+ {{- end }}
+{{- end }}
diff --git a/manifests/prometheus/grafana/templates/dashboards-json-configmap.yaml b/manifests/prometheus/grafana/templates/dashboards-json-configmap.yaml
new file mode 100644
index 0000000..8737166
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/dashboards-json-configmap.yaml
@@ -0,0 +1,22 @@
+{{- if .Values.dashboards }}
+ {{- range $provider, $dashboards := .Values.dashboards }}
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "grafana.fullname" $ }}-dashboards-{{ $provider }}
+ labels:
+ app: {{ template "grafana.name" $ }}
+ chart: {{ template "grafana.chart" $ }}
+ release: {{ $.Release.Name }}
+ heritage: {{ $.Release.Service }}
+ dashboard-provider: {{ $provider }}
+data:
+ {{- range $key, $value := $dashboards }}
+ {{- if hasKey $value "json" }}
+ {{ $key }}.json: |
+{{ $value.json | indent 4 }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+{{- end }}
diff --git a/manifests/prometheus/grafana/templates/deployment.yaml b/manifests/prometheus/grafana/templates/deployment.yaml
new file mode 100644
index 0000000..7fb428e
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/deployment.yaml
@@ -0,0 +1,270 @@
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ name: {{ template "grafana.fullname" . }}
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ template "grafana.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+{{- with .Values.annotations }}
+ annotations:
+{{ toYaml . | indent 4 }}
+{{- end }}
+spec:
+ replicas: {{ .Values.replicas }}
+ selector:
+ matchLabels:
+ app: {{ template "grafana.name" . }}
+ release: {{ .Release.Name }}
+ strategy:
+ type: {{ .Values.deploymentStrategy }}
+ {{- if ne .Values.deploymentStrategy "RollingUpdate" }}
+ rollingUpdate: null
+ {{- end }}
+ template:
+ metadata:
+ labels:
+ app: {{ template "grafana.name" . }}
+ release: {{ .Release.Name }}
+{{- with .Values.podAnnotations }}
+ annotations:
+{{ toYaml . | indent 8 }}
+{{- end }}
+ spec:
+ serviceAccountName: {{ template "grafana.serviceAccountName" . }}
+{{- if .Values.schedulerName }}
+ schedulerName: "{{ .Values.schedulerName }}"
+{{- end }}
+{{- if .Values.securityContext }}
+ securityContext:
+{{ toYaml .Values.securityContext | indent 8 }}
+{{- end }}
+{{- if .Values.dashboards }}
+ initContainers:
+ - name: download-dashboards
+ image: "{{ .Values.downloadDashboardsImage.repository }}:{{ .Values.downloadDashboardsImage.tag }}"
+ imagePullPolicy: {{ .Values.downloadDashboardsImage.pullPolicy }}
+ command: ["sh", "/etc/grafana/download_dashboards.sh"]
+ volumeMounts:
+ - name: config
+ mountPath: "/etc/grafana/download_dashboards.sh"
+ subPath: download_dashboards.sh
+ - name: storage
+ mountPath: "/var/lib/grafana"
+ subPath: {{ .Values.persistence.subPath }}
+ {{- range .Values.extraSecretMounts }}
+ - name: {{ .name }}
+ mountPath: {{ .mountPath }}
+ readOnly: {{ .readOnly }}
+ {{- end }}
+{{- end }}
+ {{- if .Values.image.pullSecrets }}
+ imagePullSecrets:
+ {{- range .Values.image.pullSecrets }}
+ - name: {{ . }}
+ {{- end}}
+ {{- end }}
+ containers:
+{{- if .Values.sidecar.dashboards.enabled }}
+ - name: {{ template "grafana.name" . }}-sc-dashboard
+ image: "{{ .Values.sidecar.image }}"
+ imagePullPolicy: {{ .Values.sidecar.imagePullPolicy }}
+ env:
+ - name: LABEL
+ value: "{{ .Values.sidecar.dashboards.label }}"
+ - name: FOLDER
+ value: "{{ .Values.sidecar.dashboards.folder }}"
+ resources:
+{{ toYaml .Values.sidecar.resources | indent 12 }}
+ volumeMounts:
+ - name: sc-dashboard-volume
+ mountPath: {{ .Values.sidecar.dashboards.folder | quote }}
+{{- end}}
+{{- if .Values.sidecar.datasources.enabled }}
+ - name: {{ template "grafana.name" . }}-sc-datasources
+ image: "{{ .Values.sidecar.image }}"
+ imagePullPolicy: {{ .Values.sidecar.imagePullPolicy }}
+ env:
+ - name: LABEL
+ value: "{{ .Values.sidecar.datasources.label }}"
+ - name: FOLDER
+ value: "/etc/grafana/provisioning/datasources"
+ resources:
+{{ toYaml .Values.sidecar.resources | indent 12 }}
+ volumeMounts:
+ - name: sc-datasources-volume
+ mountPath: "/etc/grafana/provisioning/datasources"
+{{- end}}
+ - name: {{ .Chart.Name }}
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ volumeMounts:
+ - name: config
+ mountPath: "/etc/grafana/grafana.ini"
+ subPath: grafana.ini
+ - name: ldap
+ mountPath: "/etc/grafana/ldap.toml"
+ subPath: ldap.toml
+{{- if .Values.dashboards }}
+ {{- range $provider, $dashboards := .Values.dashboards }}
+ {{- range $key, $value := $dashboards }}
+ {{- if hasKey $value "json" }}
+ - name: dashboards-{{ $provider }}
+ mountPath: "/var/lib/grafana/dashboards/{{ $provider }}/{{ $key }}.json"
+ subPath: "{{ $key }}.json"
+ {{- end }}
+ {{- end }}
+ {{- end }}
+{{- end -}}
+{{- if .Values.dashboardsConfigMaps }}
+ {{- range keys .Values.dashboardsConfigMaps }}
+ - name: dashboards-{{ . }}
+ mountPath: "/var/lib/grafana/dashboards/{{ . }}"
+ {{- end }}
+{{- end }}
+{{- if .Values.datasources }}
+ - name: config
+ mountPath: "/etc/grafana/provisioning/datasources/datasources.yaml"
+ subPath: datasources.yaml
+{{- end }}
+{{- if .Values.dashboardProviders }}
+ - name: config
+ mountPath: "/etc/grafana/provisioning/dashboards/dashboardproviders.yaml"
+ subPath: dashboardproviders.yaml
+{{- end }}
+{{- if .Values.sidecar.dashboards.enabled }}
+ - name: sc-dashboard-volume
+ mountPath: {{ .Values.sidecar.dashboards.folder | quote }}
+ - name: sc-dashboard-provider
+ mountPath: "/etc/grafana/provisioning/dashboards/sc-dashboardproviders.yaml"
+ subPath: provider.yaml
+{{- end}}
+{{- if .Values.sidecar.datasources.enabled }}
+ - name: sc-datasources-volume
+ mountPath: "/etc/grafana/provisioning/datasources"
+{{- end}}
+ - name: storage
+ mountPath: "/var/lib/grafana"
+ subPath: {{ .Values.persistence.subPath }}
+ {{- range .Values.extraSecretMounts }}
+ - name: {{ .name }}
+ mountPath: {{ .mountPath }}
+ readOnly: {{ .readOnly }}
+ {{- end }}
+ ports:
+ - name: service
+ containerPort: {{ .Values.service.port }}
+ protocol: TCP
+ - name: grafana
+ containerPort: 3000
+ protocol: TCP
+ env:
+ - name: GF_SECURITY_ADMIN_USER
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "grafana.fullname" . }}
+ key: admin-user
+ - name: GF_SECURITY_ADMIN_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "grafana.fullname" . }}
+ key: admin-password
+ {{- if .Values.plugins }}
+ - name: GF_INSTALL_PLUGINS
+ valueFrom:
+ configMapKeyRef:
+ name: {{ template "grafana.fullname" . }}
+ key: plugins
+ {{- end }}
+ {{- if .Values.smtp.existingSecret }}
+ - name: GF_SMTP_USER
+ valueFrom:
+ secretKeyRef:
+ name: {{ .Values.smtp.existingSecret }}
+ key: user
+ - name: GF_SMTP_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: {{ .Values.smtp.existingSecret }}
+ key: password
+ {{- end }}
+{{- range $key, $value := .Values.env }}
+ - name: "{{ $key }}"
+ value: "{{ $value }}"
+{{- end }}
+ {{- if .Values.envFromSecret }}
+ envFrom:
+ - secretRef:
+ name: {{ .Values.envFromSecret }}
+ {{- end }}
+ livenessProbe:
+{{ toYaml .Values.livenessProbe | indent 12 }}
+ readinessProbe:
+{{ toYaml .Values.readinessProbe | indent 12 }}
+ resources:
+{{ toYaml .Values.resources | indent 12 }}
+ {{- with .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+{{ toYaml . | indent 8 }}
+ {{- end }}
+ volumes:
+ - name: config
+ configMap:
+ name: {{ template "grafana.fullname" . }}
+ {{- if .Values.dashboards }}
+ {{- range keys .Values.dashboards }}
+ - name: dashboards-{{ . }}
+ configMap:
+ name: {{ template "grafana.fullname" $ }}-dashboards-{{ . }}
+ {{- end }}
+ {{- end }}
+ {{- if .Values.dashboardsConfigMaps }}
+ {{- range $provider, $name := .Values.dashboardsConfigMaps }}
+ - name: dashboards-{{ $provider }}
+ configMap:
+ name: {{ $name }}
+ {{- end }}
+ {{- end }}
+ - name: ldap
+ secret:
+ {{- if .Values.ldap.existingSecret }}
+ secretName: {{ .Values.ldap.existingSecret }}
+ {{- else }}
+ secretName: {{ template "grafana.fullname" . }}
+ {{- end }}
+ items:
+ - key: ldap-toml
+ path: ldap.toml
+ - name: storage
+ {{- if .Values.persistence.enabled }}
+ persistentVolumeClaim:
+ claimName: {{ .Values.persistence.existingClaim | default (include "grafana.fullname" .) }}
+ {{- else }}
+ emptyDir: {}
+ {{- end -}}
+ {{- if .Values.sidecar.dashboards.enabled }}
+ - name: sc-dashboard-volume
+ emptyDir: {}
+ - name: sc-dashboard-provider
+ configMap:
+ name: {{ template "grafana.fullname" . }}-config-dashboards
+ {{- end }}
+ {{- if .Values.sidecar.datasources.enabled }}
+ - name: sc-datasources-volume
+ emptyDir: {}
+ {{- end -}}
+ {{- range .Values.extraSecretMounts }}
+ - name: {{ .name }}
+ secret:
+ secretName: {{ .secretName }}
+ defaultMode: {{ .defaultMode }}
+ {{- end }}
diff --git a/manifests/prometheus/grafana/templates/ingress.yaml b/manifests/prometheus/grafana/templates/ingress.yaml
new file mode 100644
index 0000000..4897345
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/ingress.yaml
@@ -0,0 +1,42 @@
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "grafana.fullname" . -}}
+{{- $servicePort := .Values.service.port -}}
+{{- $ingressPath := .Values.ingress.path -}}
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+ name: {{ $fullName }}
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ template "grafana.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+{{- if .Values.ingress.labels }}
+{{ toYaml .Values.ingress.labels | indent 4 }}
+{{- end }}
+{{- with .Values.ingress.annotations }}
+ annotations:
+{{ toYaml . | indent 4 }}
+{{- end }}
+spec:
+{{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+{{- end }}
+ rules:
+ {{- range .Values.ingress.hosts }}
+ - host: {{ . }}
+ http:
+ paths:
+ - path: {{ $ingressPath }}
+ backend:
+ serviceName: {{ $fullName }}
+ servicePort: {{ $servicePort }}
+ {{- end }}
+{{- end }}
diff --git a/manifests/prometheus/grafana/templates/podsecuritypolicy.yaml b/manifests/prometheus/grafana/templates/podsecuritypolicy.yaml
new file mode 100644
index 0000000..ffe4822
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/podsecuritypolicy.yaml
@@ -0,0 +1,40 @@
+{{- if .Values.rbac.pspEnabled }}
+apiVersion: extensions/v1beta1
+kind: PodSecurityPolicy
+metadata:
+ name: {{ template "grafana.fullname" . }}
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ annotations:
+ seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
+ apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
+ seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default'
+ apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default'
+spec:
+ privileged: false
+ allowPrivilegeEscalation: false
+ requiredDropCapabilities:
+ - ALL
+ volumes:
+ - 'configMap'
+ - 'emptyDir'
+ - 'projected'
+ - 'secret'
+ - 'downwardAPI'
+ - 'persistentVolumeClaim'
+ hostNetwork: false
+ hostIPC: false
+ hostPID: false
+ runAsUser:
+ rule: 'RunAsAny'
+ seLinux:
+ rule: 'RunAsAny'
+ supplementalGroups:
+ rule: 'RunAsAny'
+ fsGroup:
+ rule: 'RunAsAny'
+ readOnlyRootFilesystem: false
+{{- end }}
diff --git a/manifests/prometheus/grafana/templates/pvc.yaml b/manifests/prometheus/grafana/templates/pvc.yaml
new file mode 100644
index 0000000..e1cc032
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/pvc.yaml
@@ -0,0 +1,24 @@
+{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: {{ template "grafana.fullname" . }}
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ template "grafana.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+ {{- with .Values.persistence.annotations }}
+ annotations:
+{{ toYaml . | indent 4 }}
+ {{- end }}
+spec:
+ accessModes:
+ {{- range .Values.persistence.accessModes }}
+ - {{ . | quote }}
+ {{- end }}
+ resources:
+ requests:
+ storage: {{ .Values.persistence.size | quote }}
+ storageClassName: {{ .Values.persistence.storageClassName }}
+{{- end -}}
diff --git a/manifests/prometheus/grafana/templates/role.yaml b/manifests/prometheus/grafana/templates/role.yaml
new file mode 100644
index 0000000..8091d49
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/role.yaml
@@ -0,0 +1,18 @@
+{{- if .Values.rbac.create }}
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: Role
+metadata:
+ name: {{ template "grafana.fullname" . }}
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+{{- if .Values.rbac.pspEnabled }}
+rules:
+- apiGroups: ['extensions']
+ resources: ['podsecuritypolicies']
+ verbs: ['use']
+ resourceNames: [{{ template "grafana.fullname" . }}]
+{{- end }}
+{{- end }}
diff --git a/manifests/prometheus/grafana/templates/rolebinding.yaml b/manifests/prometheus/grafana/templates/rolebinding.yaml
new file mode 100644
index 0000000..199e8fe
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/rolebinding.yaml
@@ -0,0 +1,18 @@
+{{- if .Values.rbac.create -}}
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: RoleBinding
+metadata:
+ name: {{ template "grafana.fullname" . }}
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: {{ template "grafana.fullname" . }}
+subjects:
+- kind: ServiceAccount
+ name: {{ template "grafana.serviceAccountName" . }}
+{{- end -}}
diff --git a/manifests/prometheus/grafana/templates/secret.yaml b/manifests/prometheus/grafana/templates/secret.yaml
new file mode 100644
index 0000000..a698e05
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/secret.yaml
@@ -0,0 +1,20 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: {{ template "grafana.fullname" . }}
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ template "grafana.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+type: Opaque
+data:
+ admin-user: {{ .Values.adminUser | b64enc | quote }}
+ {{- if .Values.adminPassword }}
+ admin-password: {{ .Values.adminPassword | b64enc | quote }}
+ {{- else }}
+ admin-password: {{ randAlphaNum 40 | b64enc | quote }}
+ {{- end }}
+ {{- if not .Values.ldap.existingSecret }}
+ ldap-toml: {{ .Values.ldap.config | b64enc | quote }}
+ {{- end }}
diff --git a/manifests/prometheus/grafana/templates/service.yaml b/manifests/prometheus/grafana/templates/service.yaml
new file mode 100644
index 0000000..6dcd63a
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/service.yaml
@@ -0,0 +1,49 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ template "grafana.fullname" . }}
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ template "grafana.chart" . }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+{{- if .Values.service.labels }}
+{{ toYaml .Values.service.labels | indent 4 }}
+{{- end }}
+{{- with .Values.service.annotations }}
+ annotations:
+{{ toYaml . | indent 4 }}
+{{- end }}
+spec:
+{{- if (or (eq .Values.service.type "ClusterIP") (empty .Values.service.type)) }}
+ type: ClusterIP
+ {{- if .Values.service.clusterIP }}
+ clusterIP: {{ .Values.service.clusterIP }}
+ {{end}}
+{{- else if eq .Values.service.type "LoadBalancer" }}
+ type: {{ .Values.service.type }}
+ {{- if .Values.service.loadBalancerIP }}
+ loadBalancerIP: {{ .Values.service.loadBalancerIP }}
+ {{- end }}
+ {{- if .Values.service.loadBalancerSourceRanges }}
+ loadBalancerSourceRanges:
+{{ toYaml .Values.service.loadBalancerSourceRanges | indent 4 }}
+ {{- end -}}
+{{- else }}
+ type: {{ .Values.service.type }}
+{{- end }}
+{{- if .Values.service.externalIPs }}
+ externalIPs:
+{{ toYaml .Values.service.externalIPs | indent 4 }}
+{{- end }}
+ ports:
+ - name: service
+ port: {{ .Values.service.port }}
+ protocol: TCP
+ targetPort: 3000
+{{ if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort))) }}
+ nodePort: {{.Values.service.nodePort}}
+{{ end }}
+ selector:
+ app: {{ template "grafana.name" . }}
+ release: {{ .Release.Name }}
diff --git a/manifests/prometheus/grafana/templates/serviceaccount.yaml b/manifests/prometheus/grafana/templates/serviceaccount.yaml
new file mode 100644
index 0000000..04601d0
--- /dev/null
+++ b/manifests/prometheus/grafana/templates/serviceaccount.yaml
@@ -0,0 +1,11 @@
+{{- if .Values.serviceAccount.create }}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app: {{ template "grafana.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "grafana.serviceAccountName" . }}
+{{- end }}
diff --git a/manifests/prometheus/grafana/values.yaml b/manifests/prometheus/grafana/values.yaml
new file mode 100644
index 0000000..0247b2f
--- /dev/null
+++ b/manifests/prometheus/grafana/values.yaml
@@ -0,0 +1,276 @@
+rbac:
+ create: true
+ pspEnabled: true
+serviceAccount:
+ create: true
+ name:
+
+replicas: 1
+
+deploymentStrategy: RollingUpdate
+
+livenessProbe:
+ httpGet:
+ path: /api/health
+ port: 3000
+
+readinessProbe:
+ httpGet:
+ path: /api/health
+ port: 3000
+ initialDelaySeconds: 60
+ timeoutSeconds: 30
+ failureThreshold: 10
+ periodSeconds: 10
+
+image:
+ repository: grafana/grafana
+ tag: 5.2.4
+ pullPolicy: IfNotPresent
+
+ ## Optionally specify an array of imagePullSecrets.
+ ## Secrets must be manually created in the namespace.
+ ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
+ ##
+ # pullSecrets:
+ # - myRegistrKeySecretName
+
+securityContext:
+ runAsUser: 472
+ fsGroup: 472
+
+downloadDashboardsImage:
+ repository: appropriate/curl
+ tag: latest
+ pullPolicy: IfNotPresent
+
+## Pod Annotations
+# podAnnotations: {}
+
+## Deployment annotations
+# annotations: {}
+
+## Expose the grafana service to be accessed from outside the cluster (LoadBalancer service).
+## or access it from within the cluster (ClusterIP service). Set the service type and the port to serve it.
+## ref: http://kubernetes.io/docs/user-guide/services/
+##
+service:
+ type: ClusterIP
+ port: 80
+ annotations: {}
+ labels: {}
+
+ingress:
+ enabled: false
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ labels: {}
+ path: /
+ hosts:
+ - chart-example.local
+ tls: []
+ # - secretName: chart-example-tls
+ # hosts:
+ # - chart-example.local
+
+resources: {}
+# limits:
+# cpu: 100m
+# memory: 128Mi
+# requests:
+# cpu: 100m
+# memory: 128Mi
+
+## Node labels for pod assignment
+## ref: https://kubernetes.io/docs/user-guide/node-selection/
+#
+nodeSelector: {}
+
+## Tolerations for pod assignment
+## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
+##
+tolerations: []
+
+## Affinity for pod assignment
+## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+##
+affinity: {}
+
+## Enable persistence using Persistent Volume Claims
+## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/
+##
+persistence:
+ enabled: false
+ # storageClassName: default
+ # accessModes:
+ # - ReadWriteOnce
+ # size: 10Gi
+ # annotations: {}
+ # subPath: ""
+ # existingClaim:
+
+adminUser: admin
+# adminPassword: strongpassword
+
+## Use an alternate scheduler, e.g. "stork".
+## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/
+##
+# schedulerName:
+
+## Extra environment variables that will be pass onto deployment pods
+env: {}
+
+## The name of a secret in the same kubernetes namespace which contain values to be added to the environment
+## This can be useful for auth tokens, etc
+envFromSecret: ""
+
+## Additional grafana server secret mounts
+# Defines additional mounts with secrets. Secrets must be manually created in the namespace.
+extraSecretMounts: []
+ # - name: secret-files
+ # mountPath: /etc/secrets
+ # secretName: grafana-secret-files
+ # readOnly: true
+
+## Pass the plugins you want installed as a list.
+##
+plugins: []
+ # - digrich-bubblechart-panel
+ # - grafana-clock-panel
+
+## Configure grafana datasources
+## ref: http://docs.grafana.org/administration/provisioning/#datasources
+##
+datasources: {}
+# datasources.yaml:
+# apiVersion: 1
+# datasources:
+# - name: Prometheus
+# type: prometheus
+# url: http://prometheus-prometheus-server
+# access: proxy
+# isDefault: true
+
+## Configure grafana dashboard providers
+## ref: http://docs.grafana.org/administration/provisioning/#dashboards
+##
+## `path` must be /var/lib/grafana/dashboards/
+##
+dashboardProviders: {}
+# dashboardproviders.yaml:
+# apiVersion: 1
+# providers:
+# - name: 'default'
+# orgId: 1
+# folder: ''
+# type: file
+# disableDeletion: false
+# editable: true
+# options:
+# path: /var/lib/grafana/dashboards/default
+
+## Configure grafana dashboard to import
+## NOTE: To use dashboards you must also enable/configure dashboardProviders
+## ref: https://grafana.com/dashboards
+##
+## dashboards per provider, use provider name as key.
+##
+dashboards: {}
+# default:
+# some-dashboard:
+# json: |
+# $RAW_JSON
+# prometheus-stats:
+# gnetId: 2
+# revision: 2
+# datasource: Prometheus
+# local-dashboard:
+# url: https://example.com/repository/test.json
+
+## Reference to external ConfigMap per provider. Use provider name as key and ConfiMap name as value.
+## A provider dashboards must be defined either by external ConfigMaps or in values.yaml, not in both.
+## ConfigMap data example:
+##
+## data:
+## example-dashboard.json: |
+## RAW_JSON
+##
+dashboardsConfigMaps: {}
+# default: ""
+
+## Grafana's primary configuration
+## NOTE: values in map will be converted to ini format
+## ref: http://docs.grafana.org/installation/configuration/
+##
+grafana.ini:
+ paths:
+ data: /var/lib/grafana/data
+ logs: /var/log/grafana
+ plugins: /var/lib/grafana/plugins
+ provisioning: /etc/grafana/provisioning
+ analytics:
+ check_for_updates: true
+ log:
+ mode: console
+ grafana_net:
+ url: https://grafana.net
+## LDAP Authentication can be enabled with the following values on grafana.ini
+## NOTE: Grafana will fail to start if the value for ldap.toml is invalid
+ # auth.ldap:
+ # enabled: true
+ # allow_sign_up: true
+ # config_file: /etc/grafana/ldap.toml
+
+## Grafana's LDAP configuration
+## Templated by the template in _helpers.tpl
+## NOTE: To enable the grafana.ini must be configured with auth.ldap.enabled
+## ref: http://docs.grafana.org/installation/configuration/#auth-ldap
+## ref: http://docs.grafana.org/installation/ldap/#configuration
+ldap:
+ # `existingSecret` is a reference to an existing secret containing the ldap configuration
+ # for Grafana in a key `ldap-toml`.
+ existingSecret: ""
+ # `config` is the content of `ldap.toml` that will be stored in the created secret
+ config: ""
+ # config: |-
+ # verbose_logging = true
+
+ # [[servers]]
+ # host = "my-ldap-server"
+ # port = 636
+ # use_ssl = true
+ # start_tls = false
+ # ssl_skip_verify = false
+ # bind_dn = "uid=%s,ou=users,dc=myorg,dc=com"
+
+## Grafana's SMTP configuration
+## NOTE: To enable, grafana.ini must be configured with smtp.enabled
+## ref: http://docs.grafana.org/installation/configuration/#smtp
+smtp:
+ # `existingSecret` is a reference to an existing secret containing the smtp configuration
+ # for Grafana in keys `user` and `password`.
+ existingSecret: ""
+
+## Sidecars that collect the configmaps with specified label and stores the included files them into the respective folders
+## Requires at least Grafana 5 to work and can't be used together with parameters dashboardProviders, datasources and dashboards
+sidecar:
+ image: kiwigrid/k8s-sidecar:0.0.3
+ imagePullPolicy: IfNotPresent
+ resources:
+# limits:
+# cpu: 100m
+# memory: 100Mi
+# requests:
+# cpu: 50m
+# memory: 50Mi
+ dashboards:
+ enabled: false
+ # label that the configmaps with dashboards are marked with
+ label: grafana_dashboard
+ # folder in the pod that should hold the collected dashboards
+ folder: /tmp/dashboards
+ datasources:
+ enabled: false
+ # label that the configmaps with datasources are marked with
+ label: grafana_datasource
diff --git a/manifests/prometheus/prom-alertrules.yaml b/manifests/prometheus/prom-alertrules.yaml
new file mode 100644
index 0000000..27492cb
--- /dev/null
+++ b/manifests/prometheus/prom-alertrules.yaml
@@ -0,0 +1,21 @@
+serverFiles:
+ alerts:
+ groups:
+ - name: k8s_alert_rules
+ rules:
+ # ALERT when container memory usage exceed 90%
+ - alert: container_mem_over_90
+ expr: (sum(container_memory_working_set_bytes{image!="",name=~"^k8s_.*", pod_name!=""}) by (pod_name)) / (sum (container_spec_memory_limit_bytes{image!="",name=~"^k8s_.*", pod_name!=""}) by (pod_name)) > 0.9 and (sum(container_memory_working_set_bytes{image!="",name=~"^k8s_.*", pod_name!=""}) by (pod_name)) / (sum (container_spec_memory_limit_bytes{image!="",name=~"^k8s_.*", pod_name!=""}) by (pod_name)) < 2
+ for: 2m
+ annotations:
+ summary: "{{ $labels.pod_name }}'s memory usage alert"
+ description: "Memory Usage of Pod {{ $labels.pod_name }} on {{ $labels.kubernetes_io_hostname }} has exceeded 90% for more than 2 minutes."
+
+ # ALERT when node is down
+ - alert: node_down
+ expr: up == 0
+ for: 60s
+ annotations:
+ summary: "Node {{ $labels.kubernetes_io_hostname }} is down"
+ description: "Node {{ $labels.kubernetes_io_hostname }} is down"
+
diff --git a/manifests/prometheus/prom-alertsmanager.yaml b/manifests/prometheus/prom-alertsmanager.yaml
new file mode 100644
index 0000000..9ca7897
--- /dev/null
+++ b/manifests/prometheus/prom-alertsmanager.yaml
@@ -0,0 +1,27 @@
+alertmanagerFiles:
+ alertmanager.yml:
+ global:
+ smtp_smarthost: 'smtp.163.com:25'
+ smtp_from: 'xxxx@163.com'
+ smtp_auth_username: 'xxxx@163.com'
+ smtp_auth_password: '*********'
+ smtp_require_tls: false
+
+ route:
+ group_by: ['alertname', 'pod_name']
+ group_wait: 10s
+ group_interval: 5m
+ #receiver: AlertMail
+ receiver: dingtalk
+ repeat_interval: 3h
+
+ receivers:
+ - name: 'AlertMail'
+ email_configs:
+ - to: 'xxxx@163.com'
+ - name: dingtalk
+ webhook_configs:
+ - send_resolved: false
+ # 需要运行插件 dingtalk-webhook.yaml,详情阅读 docs/guide/prometheus.md
+ url: http://webhook-dingtalk.monitoring.svc.cluster.local:8060/dingtalk/webhook1/send
+
diff --git a/manifests/prometheus/prom-settings.yaml b/manifests/prometheus/prom-settings.yaml
new file mode 100644
index 0000000..9cb7afe
--- /dev/null
+++ b/manifests/prometheus/prom-settings.yaml
@@ -0,0 +1,20 @@
+alertmanager:
+ persistentVolume:
+ enabled: false
+ service:
+ type: NodePort
+ nodePort: 39001
+
+server:
+ persistentVolume:
+ enabled: false
+ service:
+ type: NodePort
+ nodePort: 39000
+
+pushgateway:
+ enabled: false
+
+kubeStateMetrics:
+ image:
+ repository: mirrorgooglecontainers/kube-state-metrics
diff --git a/manifests/prometheus/prometheus/.helmignore b/manifests/prometheus/prometheus/.helmignore
new file mode 100644
index 0000000..f0c1319
--- /dev/null
+++ b/manifests/prometheus/prometheus/.helmignore
@@ -0,0 +1,21 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
diff --git a/manifests/prometheus/prometheus/Chart.yaml b/manifests/prometheus/prometheus/Chart.yaml
new file mode 100755
index 0000000..894afdb
--- /dev/null
+++ b/manifests/prometheus/prometheus/Chart.yaml
@@ -0,0 +1,19 @@
+name: prometheus
+version: 7.1.4
+appVersion: 2.4.3
+description: Prometheus is a monitoring system and time series database.
+home: https://prometheus.io/
+icon: https://raw.githubusercontent.com/prometheus/prometheus.github.io/master/assets/prometheus_logo-cb55bb5c346.png
+sources:
+ - https://github.com/prometheus/alertmanager
+ - https://github.com/prometheus/prometheus
+ - https://github.com/prometheus/pushgateway
+ - https://github.com/prometheus/node_exporter
+ - https://github.com/kubernetes/kube-state-metrics
+maintainers:
+ - name: mgoodness
+ email: mgoodness@gmail.com
+ - name: gianrubio
+ email: gianrubio@gmail.com
+engine: gotpl
+tillerVersion: ">=2.8.0"
diff --git a/manifests/prometheus/prometheus/OWNERS b/manifests/prometheus/prometheus/OWNERS
new file mode 100644
index 0000000..0cfd950
--- /dev/null
+++ b/manifests/prometheus/prometheus/OWNERS
@@ -0,0 +1,6 @@
+approvers:
+- mgoodness
+- gianrubio
+reviewers:
+- mgoodness
+- gianrubio
diff --git a/manifests/prometheus/prometheus/README.md b/manifests/prometheus/prometheus/README.md
new file mode 100644
index 0000000..0a41149
--- /dev/null
+++ b/manifests/prometheus/prometheus/README.md
@@ -0,0 +1,349 @@
+# Prometheus
+
+[Prometheus](https://prometheus.io/), a [Cloud Native Computing Foundation](https://cncf.io/) project, is a systems and service monitoring system. It collects metrics from configured targets at given intervals, evaluates rule expressions, displays the results, and can trigger alerts if some condition is observed to be true.
+
+## TL;DR;
+
+```console
+$ helm install stable/prometheus
+```
+
+## Introduction
+
+This chart bootstraps a [Prometheus](https://prometheus.io/) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
+
+## Prerequisites
+
+- Kubernetes 1.3+ with Beta APIs enabled
+
+## Installing the Chart
+
+To install the chart with the release name `my-release`:
+
+```console
+$ helm install --name my-release stable/prometheus
+```
+
+The command deploys Prometheus on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation.
+
+> **Tip**: List all releases using `helm list`
+
+## Uninstalling the Chart
+
+To uninstall/delete the `my-release` deployment:
+
+```console
+$ helm delete my-release
+```
+
+The command removes all the Kubernetes components associated with the chart and deletes the release.
+
+## Prometheus 2.x
+
+Prometheus version 2.x has made changes to alertmanager, storage and recording rules. Check out the migration guide [here](https://prometheus.io/docs/prometheus/2.0/migration/)
+
+Users of this chart will need to update their alerting rules to the new format before they can upgrade.
+
+## Upgrading from previous chart versions.
+
+As of version 5.0, this chart uses Prometheus 2.1. This version of prometheus introduces a new data format and is not compatible with prometheus 1.x. It is recommended to install this as a new release, as updating existing releases will not work. See the [prometheus docs](https://prometheus.io/docs/prometheus/latest/migration/#storage) for instructions on retaining your old data.
+
+### Example migration
+
+Assuming you have an existing release of the prometheus chart, named `prometheus-old`. In order to update to prometheus 2.1 while keeping your old data do the following:
+
+1. Update the `prometheus-old` release. Disable scraping on every component besides the prometheus server, similar to the configuration below:
+
+ ```
+ alertmanager:
+ enabled: false
+ alertmanagerFiles:
+ alertmanager.yml: ""
+ kubeStateMetrics:
+ enabled: false
+ nodeExporter:
+ enabled: false
+ pushgateway:
+ enabled: false
+ server:
+ extraArgs:
+ storage.local.retention: 720h
+ serverFiles:
+ alerts: ""
+ prometheus.yml: ""
+ rules: ""
+ ```
+
+1. Deploy a new release of the chart with version 5.0+ using prometheus 2.x. In the values.yaml set the scrape config as usual, and also add the `prometheus-old` instance as a remote-read target.
+
+ ```
+ prometheus.yml:
+ ...
+ remote_read:
+ - url: http://prometheus-old/api/v1/read
+ ...
+ ```
+
+ Old data will be available when you query the new prometheus instance.
+
+## Configuration
+
+The following table lists the configurable parameters of the Prometheus chart and their default values.
+
+Parameter | Description | Default
+--------- | ----------- | -------
+`alertmanager.enabled` | If true, create alertmanager | `true`
+`alertmanager.name` | alertmanager container name | `alertmanager`
+`alertmanager.image.repository` | alertmanager container image repository | `prom/alertmanager`
+`alertmanager.image.tag` | alertmanager container image tag | `v0.15.2`
+`alertmanager.image.pullPolicy` | alertmanager container image pull policy | `IfNotPresent`
+`alertmanager.prefixURL` | The prefix slug at which the server can be accessed | ``
+`alertmanager.baseURL` | The external url at which the server can be accessed | `/`
+`alertmanager.extraArgs` | Additional alertmanager container arguments | `{}`
+`alertmanager.configMapOverrideName` | Prometheus alertmanager ConfigMap override where full-name is `{{.Release.Name}}-{{.Values.alertmanager.configMapOverrideName}}` and setting this value will prevent the default alertmanager ConfigMap from being generated | `""`
+`alertmanager.ingress.enabled` | If true, alertmanager Ingress will be created | `false`
+`alertmanager.ingress.annotations` | alertmanager Ingress annotations | `{}`
+`alertmanager.ingress.extraLabels` | alertmanager Ingress additional labels | `{}`
+`alertmanager.ingress.hosts` | alertmanager Ingress hostnames | `[]`
+`alertmanager.ingress.tls` | alertmanager Ingress TLS configuration (YAML) | `[]`
+`alertmanager.nodeSelector` | node labels for alertmanager pod assignment | `{}`
+`alertmanager.tolerations` | node taints to tolerate (requires Kubernetes >=1.6) | `[]`
+`alertmanager.affinity` | pod affinity | `{}`
+`alertmanager.schedulerName` | alertmanager alternate scheduler name | `nil`
+`alertmanager.persistentVolume.enabled` | If true, alertmanager will create a Persistent Volume Claim | `true`
+`alertmanager.persistentVolume.accessModes` | alertmanager data Persistent Volume access modes | `[ReadWriteOnce]`
+`alertmanager.persistentVolume.annotations` | Annotations for alertmanager Persistent Volume Claim | `{}`
+`alertmanager.persistentVolume.existingClaim` | alertmanager data Persistent Volume existing claim name | `""`
+`alertmanager.persistentVolume.mountPath` | alertmanager data Persistent Volume mount root path | `/data`
+`alertmanager.persistentVolume.size` | alertmanager data Persistent Volume size | `2Gi`
+`alertmanager.persistentVolume.storageClass` | alertmanager data Persistent Volume Storage Class | `unset`
+`alertmanager.persistentVolume.subPath` | Subdirectory of alertmanager data Persistent Volume to mount | `""`
+`alertmanager.podAnnotations` | annotations to be added to alertmanager pods | `{}`
+`alertmanager.replicaCount` | desired number of alertmanager pods | `1`
+`alertmanager.priorityClassName` | alertmanager priorityClassName | `nil`
+`alertmanager.resources` | alertmanager pod resource requests & limits | `{}`
+`alertmanager.securityContext` | Custom [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) for Alert Manager containers | `{}`
+`alertmanager.service.annotations` | annotations for alertmanager service | `{}`
+`alertmanager.service.clusterIP` | internal alertmanager cluster service IP | `""`
+`alertmanager.service.externalIPs` | alertmanager service external IP addresses | `[]`
+`alertmanager.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""`
+`alertmanager.service.loadBalancerSourceRanges` | list of IP CIDRs allowed access to load balancer (if supported) | `[]`
+`alertmanager.service.servicePort` | alertmanager service port | `80`
+`alertmanager.service.type` | type of alertmanager service to create | `ClusterIP`
+`alertmanagerFiles.alertmanager.yml` | Prometheus alertmanager configuration | example configuration
+`configmapReload.name` | configmap-reload container name | `configmap-reload`
+`configmapReload.image.repository` | configmap-reload container image repository | `jimmidyson/configmap-reload`
+`configmapReload.image.tag` | configmap-reload container image tag | `v0.2.2`
+`configmapReload.image.pullPolicy` | configmap-reload container image pull policy | `IfNotPresent`
+`configmapReload.extraArgs` | Additional configmap-reload container arguments | `{}`
+`configmapReload.extraConfigmapMounts` | Additional configmap-reload configMap mounts | `[]`
+`configmapReload.resources` | configmap-reload pod resource requests & limits | `{}`
+`initChownData.enabled` | If false, don't reset data ownership at startup | true
+`initChownData.name` | init-chown-data container name | `init-chown-data`
+`initChownData.image.repository` | init-chown-data container image repository | `busybox`
+`initChownData.image.tag` | init-chown-data container image tag | `latest`
+`initChownData.image.pullPolicy` | init-chown-data container image pull policy | `IfNotPresent`
+`initChownData.resources` | init-chown-data pod resource requests & limits | `{}`
+`kubeStateMetrics.enabled` | If true, create kube-state-metrics | `true`
+`kubeStateMetrics.name` | kube-state-metrics container name | `kube-state-metrics`
+`kubeStateMetrics.image.repository` | kube-state-metrics container image repository| `quay.io/coreos/kube-state-metrics`
+`kubeStateMetrics.image.tag` | kube-state-metrics container image tag | `v1.4.0`
+`kubeStateMetrics.image.pullPolicy` | kube-state-metrics container image pull policy | `IfNotPresent`
+`kubeStateMetrics.args` | kube-state-metrics container arguments | `{}`
+`kubeStateMetrics.nodeSelector` | node labels for kube-state-metrics pod assignment | `{}`
+`kubeStateMetrics.podAnnotations` | annotations to be added to kube-state-metrics pods | `{}`
+`kubeStateMetrics.deploymentAnnotations` | annotations to be added to kube-state-metrics deployment | `{}`
+`kubeStateMetrics.tolerations` | node taints to tolerate (requires Kubernetes >=1.6) | `[]`
+`kubeStateMetrics.replicaCount` | desired number of kube-state-metrics pods | `1`
+`kubeStateMetrics.priorityClassName` | kube-state-metrics priorityClassName | `nil`
+`kubeStateMetrics.resources` | kube-state-metrics resource requests and limits (YAML) | `{}`
+`kubeStateMetrics.securityContext` | Custom [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) for kube-state-metrics containers | `{}`
+`kubeStateMetrics.service.annotations` | annotations for kube-state-metrics service | `{prometheus.io/scrape: "true"}`
+`kubeStateMetrics.service.clusterIP` | internal kube-state-metrics cluster service IP | `None`
+`kubeStateMetrics.service.externalIPs` | kube-state-metrics service external IP addresses | `[]`
+`kubeStateMetrics.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""`
+`kubeStateMetrics.service.loadBalancerSourceRanges` | list of IP CIDRs allowed access to load balancer (if supported) | `[]`
+`kubeStateMetrics.service.servicePort` | kube-state-metrics service port | `80`
+`kubeStateMetrics.service.type` | type of kube-state-metrics service to create | `ClusterIP`
+`nodeExporter.enabled` | If true, create node-exporter | `true`
+`nodeExporter.name` | node-exporter container name | `node-exporter`
+`nodeExporter.image.repository` | node-exporter container image repository| `prom/node-exporter`
+`nodeExporter.image.tag` | node-exporter container image tag | `v0.16.0`
+`nodeExporter.image.pullPolicy` | node-exporter container image pull policy | `IfNotPresent`
+`nodeExporter.extraArgs` | Additional node-exporter container arguments | `{}`
+`nodeExporter.extraHostPathMounts` | Additional node-exporter hostPath mounts | `[]`
+`nodeExporter.extraConfigmapMounts` | Additional node-exporter configMap mounts | `[]`
+`nodeExporter.hostNetwork` | If true, node-exporter pods share the host network namespace | `true`
+`nodeExporter.hostPID` | If true, node-exporter pods share the host PID namespace | `true`
+`nodeExporter.nodeSelector` | node labels for node-exporter pod assignment | `{}`
+`nodeExporter.podAnnotations` | annotations to be added to node-exporter pods | `{}`
+`nodeExporter.pod.labels` | labels to be added to node-exporter pods | `{}`
+`nodeExporter.tolerations` | node taints to tolerate (requires Kubernetes >=1.6) | `[]`
+`nodeExporter.priorityClassName` | node-exporter priorityClassName | `nil`
+`nodeExporter.resources` | node-exporter resource requests and limits (YAML) | `{}`
+`nodeExporter.securityContext` | securityContext for containers in pod | `{}`
+`nodeExporter.service.annotations` | annotations for node-exporter service | `{prometheus.io/scrape: "true"}`
+`nodeExporter.service.clusterIP` | internal node-exporter cluster service IP | `None`
+`nodeExporter.service.externalIPs` | node-exporter service external IP addresses | `[]`
+`nodeExporter.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""`
+`nodeExporter.service.loadBalancerSourceRanges` | list of IP CIDRs allowed access to load balancer (if supported) | `[]`
+`nodeExporter.service.servicePort` | node-exporter service port | `9100`
+`nodeExporter.service.type` | type of node-exporter service to create | `ClusterIP`
+`pushgateway.enabled` | If true, create pushgateway | `true`
+`pushgateway.name` | pushgateway container name | `pushgateway`
+`pushgateway.image.repository` | pushgateway container image repository | `prom/pushgateway`
+`pushgateway.image.tag` | pushgateway container image tag | `v0.5.2`
+`pushgateway.image.pullPolicy` | pushgateway container image pull policy | `IfNotPresent`
+`pushgateway.extraArgs` | Additional pushgateway container arguments | `{}`
+`pushgateway.ingress.enabled` | If true, pushgateway Ingress will be created | `false`
+`pushgateway.ingress.annotations` | pushgateway Ingress annotations | `{}`
+`pushgateway.ingress.hosts` | pushgateway Ingress hostnames | `[]`
+`pushgateway.ingress.tls` | pushgateway Ingress TLS configuration (YAML) | `[]`
+`pushgateway.nodeSelector` | node labels for pushgateway pod assignment | `{}`
+`pushgateway.podAnnotations` | annotations to be added to pushgateway pods | `{}`
+`pushgateway.tolerations` | node taints to tolerate (requires Kubernetes >=1.6) | `[]`
+`pushgateway.replicaCount` | desired number of pushgateway pods | `1`
+`pushgateway.priorityClassName` | pushgateway priorityClassName | `nil`
+`pushgateway.resources` | pushgateway pod resource requests & limits | `{}`
+`pushgateway.service.annotations` | annotations for pushgateway service | `{}`
+`pushgateway.service.clusterIP` | internal pushgateway cluster service IP | `""`
+`pushgateway.service.externalIPs` | pushgateway service external IP addresses | `[]`
+`pushgateway.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""`
+`pushgateway.service.loadBalancerSourceRanges` | list of IP CIDRs allowed access to load balancer (if supported) | `[]`
+`pushgateway.service.servicePort` | pushgateway service port | `9091`
+`pushgateway.service.type` | type of pushgateway service to create | `ClusterIP`
+`rbac.create` | If true, create & use RBAC resources | `true`
+`server.name` | Prometheus server container name | `server`
+`server.image.repository` | Prometheus server container image repository | `prom/prometheus`
+`server.image.tag` | Prometheus server container image tag | `v2.4.3`
+`server.image.pullPolicy` | Prometheus server container image pull policy | `IfNotPresent`
+`server.enableAdminApi` | If true, Prometheus administrative HTTP API will be enabled. Please note, that you should take care of administrative API access protection (ingress or some frontend Nginx with auth) before enabling it. | `false`
+`server.global.scrape_interval` | How frequently to scrape targets by default | `1m`
+`server.global.scrape_timeout` | How long until a scrape request times out | `10s`
+`server.global.evaluation_interval` | How frequently to evaluate rules | `1m`
+`server.extraArgs` | Additional Prometheus server container arguments | `{}`
+`server.prefixURL` | The prefix slug at which the server can be accessed | ``
+`server.baseURL` | The external url at which the server can be accessed | ``
+`server.extraHostPathMounts` | Additional Prometheus server hostPath mounts | `[]`
+`server.extraConfigmapMounts` | Additional Prometheus server configMap mounts | `[]`
+`server.extraSecretMounts` | Additional Prometheus server Secret mounts | `[]`
+`server.configMapOverrideName` | Prometheus server ConfigMap override where full-name is `{{.Release.Name}}-{{.Values.server.configMapOverrideName}}` and setting this value will prevent the default server ConfigMap from being generated | `""`
+`server.ingress.enabled` | If true, Prometheus server Ingress will be created | `false`
+`server.ingress.annotations` | Prometheus server Ingress annotations | `[]`
+`server.ingress.extraLabels` | Prometheus server Ingress additional labels | `{}`
+`server.ingress.hosts` | Prometheus server Ingress hostnames | `[]`
+`server.ingress.tls` | Prometheus server Ingress TLS configuration (YAML) | `[]`
+`server.nodeSelector` | node labels for Prometheus server pod assignment | `{}`
+`server.tolerations` | node taints to tolerate (requires Kubernetes >=1.6) | `[]`
+`server.affinity` | pod affinity | `{}`
+`server.priorityClassName` | Prometheus server priorityClassName | `nil`
+`server.schedulerName` | Prometheus server alternate scheduler name | `nil`
+`server.persistentVolume.enabled` | If true, Prometheus server will create a Persistent Volume Claim | `true`
+`server.persistentVolume.accessModes` | Prometheus server data Persistent Volume access modes | `[ReadWriteOnce]`
+`server.persistentVolume.annotations` | Prometheus server data Persistent Volume annotations | `{}`
+`server.persistentVolume.existingClaim` | Prometheus server data Persistent Volume existing claim name | `""`
+`server.persistentVolume.mountPath` | Prometheus server data Persistent Volume mount root path | `/data`
+`server.persistentVolume.size` | Prometheus server data Persistent Volume size | `8Gi`
+`server.persistentVolume.storageClass` | Prometheus server data Persistent Volume Storage Class | `unset`
+`server.persistentVolume.subPath` | Subdirectory of Prometheus server data Persistent Volume to mount | `""`
+`server.podAnnotations` | annotations to be added to Prometheus server pods | `{}`
+`server.deploymentAnnotations` | annotations to be added to Prometheus server deployment | `{}'
+`server.replicaCount` | desired number of Prometheus server pods | `1`
+`server.resources` | Prometheus server resource requests and limits | `{}`
+`server.securityContext` | Custom [security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) for server containers | `{}`
+`server.service.annotations` | annotations for Prometheus server service | `{}`
+`server.service.clusterIP` | internal Prometheus server cluster service IP | `""`
+`server.service.externalIPs` | Prometheus server service external IP addresses | `[]`
+`server.service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""`
+`server.service.loadBalancerSourceRanges` | list of IP CIDRs allowed access to load balancer (if supported) | `[]`
+`server.service.nodePort` | Port to be used as the service NodePort (ignored if `server.service.type` is not `NodePort`) | `0`
+`server.service.servicePort` | Prometheus server service port | `80`
+`server.service.type` | type of Prometheus server service to create | `ClusterIP`
+`serviceAccounts.alertmanager.create` | If true, create the alertmanager service account | `true`
+`serviceAccounts.alertmanager.name` | name of the alertmanager service account to use or create | `{{ prometheus.alertmanager.fullname }}`
+`serviceAccounts.kubeStateMetrics.create` | If true, create the kubeStateMetrics service account | `true`
+`serviceAccounts.kubeStateMetrics.name` | name of the kubeStateMetrics service account to use or create | `{{ prometheus.kubeStateMetrics.fullname }}`
+`serviceAccounts.nodeExporter.create` | If true, create the nodeExporter service account | `true`
+`serviceAccounts.nodeExporter.name` | name of the nodeExporter service account to use or create | `{{ prometheus.nodeExporter.fullname }}`
+`serviceAccounts.pushgateway.create` | If true, create the pushgateway service account | `true`
+`serviceAccounts.pushgateway.name` | name of the pushgateway service account to use or create | `{{ prometheus.pushgateway.fullname }}`
+`serviceAccounts.server.create` | If true, create the server service account | `true`
+`serviceAccounts.server.name` | name of the server service account to use or create | `{{ prometheus.server.fullname }}`
+`server.terminationGracePeriodSeconds` | Prometheus server Pod termination grace period | `300`
+`server.retention` | (optional) Prometheus data retention | `""`
+`serverFiles.alerts` | Prometheus server alerts configuration | `{}`
+`serverFiles.rules` | Prometheus server rules configuration | `{}`
+`serverFiles.prometheus.yml` | Prometheus server scrape configuration | example configuration
+`networkPolicy.enabled` | Enable NetworkPolicy | `false` |
+
+Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
+
+```console
+$ helm install stable/prometheus --name my-release \
+ --set server.terminationGracePeriodSeconds=360
+```
+
+Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
+
+```console
+$ helm install stable/prometheus --name my-release -f values.yaml
+```
+
+> **Tip**: You can use the default [values.yaml](values.yaml)
+
+### RBAC Configuration
+Roles and RoleBindings resources will be created automatically for `server` and `kubeStateMetrics` services.
+
+To manually setup RBAC you need to set the parameter `rbac.create=false` and specify the service account to be used for each service by setting the parameters: `serviceAccounts.{{ component }}.create` to `false` and `serviceAccounts.{{ component }}.name` to the name of a pre-existing service account.
+
+> **Tip**: You can refer to the default `*-clusterrole.yaml` and `*-clusterrolebinding.yaml` files in [templates](templates/) to customize your own.
+
+### ConfigMap Files
+AlertManager is configured through [alertmanager.yml](https://prometheus.io/docs/alerting/configuration/). This file (and any others listed in `alertmanagerFiles`) will be mounted into the `alertmanager` pod.
+
+Prometheus is configured through [prometheus.yml](https://prometheus.io/docs/operating/configuration/). This file (and any others listed in `serverFiles`) will be mounted into the `server` pod.
+
+### Ingress TLS
+If your cluster allows automatic creation/retrieval of TLS certificates (e.g. [kube-lego](https://github.com/jetstack/kube-lego)), please refer to the documentation for that mechanism.
+
+To manually configure TLS, first create/retrieve a key & certificate pair for the address(es) you wish to protect. Then create a TLS secret in the namespace:
+
+```console
+kubectl create secret tls prometheus-server-tls --cert=path/to/tls.cert --key=path/to/tls.key
+```
+
+Include the secret's name, along with the desired hostnames, in the alertmanager/server Ingress TLS section of your custom `values.yaml` file:
+
+```yaml
+server:
+ ingress:
+ ## If true, Prometheus server Ingress will be created
+ ##
+ enabled: true
+
+ ## Prometheus server Ingress hostnames
+ ## Must be provided if Ingress is enabled
+ ##
+ hosts:
+ - prometheus.domain.com
+
+ ## Prometheus server Ingress TLS configuration
+ ## Secrets must be manually created in the namespace
+ ##
+ tls:
+ - secretName: prometheus-server-tls
+ hosts:
+ - prometheus.domain.com
+```
+
+### NetworkPolicy
+
+Enabling Network Policy for Prometheus will secure connections to Alert Manager
+and Kube State Metrics by only accepting connections from Prometheus Server.
+All inbound connections to Prometheus Server are still allowed.
+
+To enable network policy for Prometheus, install a networking plugin that
+implements the Kubernetes NetworkPolicy spec, and set `networkPolicy.enabled` to true.
+
+If NetworkPolicy is enabled for Prometheus' scrape targets, you may also need
+to manually create a networkpolicy which allows it.
diff --git a/manifests/prometheus/prometheus/templates/NOTES.txt b/manifests/prometheus/prometheus/templates/NOTES.txt
new file mode 100644
index 0000000..289e384
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/NOTES.txt
@@ -0,0 +1,100 @@
+The Prometheus server can be accessed via port {{ .Values.server.service.servicePort }} on the following DNS name from within your cluster:
+{{ template "prometheus.server.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
+
+{{ if .Values.server.ingress.enabled -}}
+From outside the cluster, the server URL(s) are:
+{{- range .Values.server.ingress.hosts }}
+http://{{ . }}
+{{- end }}
+{{- else }}
+Get the Prometheus server URL by running these commands in the same shell:
+{{- if contains "NodePort" .Values.server.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "prometheus.server.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.server.service.type }}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "prometheus.server.fullname" . }}'
+
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "prometheus.server.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:{{ .Values.server.service.servicePort }}
+{{- else if contains "ClusterIP" .Values.server.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "prometheus.name" . }},component={{ .Values.server.name }}" -o jsonpath="{.items[0].metadata.name}")
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 9090
+{{- end }}
+{{- end }}
+
+{{- if .Values.server.persistentVolume.enabled }}
+{{- else }}
+#################################################################################
+###### WARNING: Persistence is disabled!!! You will lose your data when #####
+###### the Server pod is terminated. #####
+#################################################################################
+{{- end }}
+
+{{ if .Values.alertmanager.enabled }}
+The Prometheus alertmanager can be accessed via port {{ .Values.alertmanager.service.servicePort }} on the following DNS name from within your cluster:
+{{ template "prometheus.alertmanager.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
+
+{{ if .Values.alertmanager.ingress.enabled -}}
+From outside the cluster, the alertmanager URL(s) are:
+{{- range .Values.alertmanager.ingress.hosts }}
+http://{{ . }}
+{{- end }}
+{{- else }}
+Get the Alertmanager URL by running these commands in the same shell:
+{{- if contains "NodePort" .Values.alertmanager.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "prometheus.alertmanager.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.alertmanager.service.type }}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "prometheus.alertmanager.fullname" . }}'
+
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "prometheus.alertmanager.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:{{ .Values.alertmanager.service.servicePort }}
+{{- else if contains "ClusterIP" .Values.alertmanager.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "prometheus.name" . }},component={{ .Values.alertmanager.name }}" -o jsonpath="{.items[0].metadata.name}")
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 9093
+{{- end }}
+{{- end }}
+
+{{- if .Values.alertmanager.persistentVolume.enabled }}
+{{- else }}
+#################################################################################
+###### WARNING: Persistence is disabled!!! You will lose your data when #####
+###### the AlertManager pod is terminated. #####
+#################################################################################
+{{- end }}
+{{- end }}
+
+{{ if .Values.pushgateway.enabled }}
+The Prometheus PushGateway can be accessed via port {{ .Values.pushgateway.service.servicePort }} on the following DNS name from within your cluster:
+{{ template "prometheus.pushgateway.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
+
+{{ if .Values.pushgateway.ingress.enabled -}}
+From outside the cluster, the pushgateway URL(s) are:
+{{- range .Values.pushgateway.ingress.hosts }}
+http://{{ . }}
+{{- end }}
+{{- else }}
+Get the PushGateway URL by running these commands in the same shell:
+{{- if contains "NodePort" .Values.pushgateway.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "prometheus.pushgateway.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.pushgateway.service.type }}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "prometheus.pushgateway.fullname" . }}'
+
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "prometheus.pushgateway.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
+ echo http://$SERVICE_IP:{{ .Values.pushgateway.service.servicePort }}
+{{- else if contains "ClusterIP" .Values.pushgateway.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "prometheus.name" . }},component={{ .Values.pushgateway.name }}" -o jsonpath="{.items[0].metadata.name}")
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 9091
+{{- end }}
+{{- end }}
+{{- end }}
+
+For more information on running Prometheus, visit:
+https://prometheus.io/
diff --git a/manifests/prometheus/prometheus/templates/_helpers.tpl b/manifests/prometheus/prometheus/templates/_helpers.tpl
new file mode 100644
index 0000000..783c76e
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/_helpers.tpl
@@ -0,0 +1,176 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "prometheus.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "prometheus.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create a fully qualified alertmanager name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+
+{{- define "prometheus.alertmanager.fullname" -}}
+{{- if .Values.alertmanager.fullnameOverride -}}
+{{- .Values.alertmanager.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- printf "%s-%s" .Release.Name .Values.alertmanager.name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s-%s" .Release.Name $name .Values.alertmanager.name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create a fully qualified kube-state-metrics name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "prometheus.kubeStateMetrics.fullname" -}}
+{{- if .Values.kubeStateMetrics.fullnameOverride -}}
+{{- .Values.kubeStateMetrics.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- printf "%s-%s" .Release.Name .Values.kubeStateMetrics.name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s-%s" .Release.Name $name .Values.kubeStateMetrics.name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create a fully qualified node-exporter name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "prometheus.nodeExporter.fullname" -}}
+{{- if .Values.nodeExporter.fullnameOverride -}}
+{{- .Values.nodeExporter.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- printf "%s-%s" .Release.Name .Values.nodeExporter.name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s-%s" .Release.Name $name .Values.nodeExporter.name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create a fully qualified Prometheus server name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "prometheus.server.fullname" -}}
+{{- if .Values.server.fullnameOverride -}}
+{{- .Values.server.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- printf "%s-%s" .Release.Name .Values.server.name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s-%s" .Release.Name $name .Values.server.name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create a fully qualified pushgateway name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "prometheus.pushgateway.fullname" -}}
+{{- if .Values.pushgateway.fullnameOverride -}}
+{{- .Values.pushgateway.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- printf "%s-%s" .Release.Name .Values.pushgateway.name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s-%s" .Release.Name $name .Values.pushgateway.name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Return the appropriate apiVersion for networkpolicy.
+*/}}
+{{- define "prometheus.networkPolicy.apiVersion" -}}
+{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.GitVersion -}}
+{{- print "extensions/v1beta1" -}}
+{{- else if semverCompare "^1.7-0" .Capabilities.KubeVersion.GitVersion -}}
+{{- print "networking.k8s.io/v1" -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create the name of the service account to use for the alertmanager component
+*/}}
+{{- define "prometheus.serviceAccountName.alertmanager" -}}
+{{- if .Values.serviceAccounts.alertmanager.create -}}
+ {{ default (include "prometheus.alertmanager.fullname" .) .Values.serviceAccounts.alertmanager.name }}
+{{- else -}}
+ {{ default "default" .Values.serviceAccounts.alertmanager.name }}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create the name of the service account to use for the kubeStateMetrics component
+*/}}
+{{- define "prometheus.serviceAccountName.kubeStateMetrics" -}}
+{{- if .Values.serviceAccounts.kubeStateMetrics.create -}}
+ {{ default (include "prometheus.kubeStateMetrics.fullname" .) .Values.serviceAccounts.kubeStateMetrics.name }}
+{{- else -}}
+ {{ default "default" .Values.serviceAccounts.kubeStateMetrics.name }}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create the name of the service account to use for the nodeExporter component
+*/}}
+{{- define "prometheus.serviceAccountName.nodeExporter" -}}
+{{- if .Values.serviceAccounts.nodeExporter.create -}}
+ {{ default (include "prometheus.nodeExporter.fullname" .) .Values.serviceAccounts.nodeExporter.name }}
+{{- else -}}
+ {{ default "default" .Values.serviceAccounts.nodeExporter.name }}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create the name of the service account to use for the pushgateway component
+*/}}
+{{- define "prometheus.serviceAccountName.pushgateway" -}}
+{{- if .Values.serviceAccounts.pushgateway.create -}}
+ {{ default (include "prometheus.pushgateway.fullname" .) .Values.serviceAccounts.pushgateway.name }}
+{{- else -}}
+ {{ default "default" .Values.serviceAccounts.pushgateway.name }}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create the name of the service account to use for the server component
+*/}}
+{{- define "prometheus.serviceAccountName.server" -}}
+{{- if .Values.serviceAccounts.server.create -}}
+ {{ default (include "prometheus.server.fullname" .) .Values.serviceAccounts.server.name }}
+{{- else -}}
+ {{ default "default" .Values.serviceAccounts.server.name }}
+{{- end -}}
+{{- end -}}
diff --git a/manifests/prometheus/prometheus/templates/alertmanager-configmap.yaml b/manifests/prometheus/prometheus/templates/alertmanager-configmap.yaml
new file mode 100644
index 0000000..0775a55
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/alertmanager-configmap.yaml
@@ -0,0 +1,18 @@
+{{- if and .Values.alertmanager.enabled (empty .Values.alertmanager.configMapOverrideName) -}}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.alertmanager.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.alertmanager.fullname" . }}
+data:
+{{- $root := . -}}
+{{- range $key, $value := .Values.alertmanagerFiles }}
+ {{ $key }}: |
+{{ toYaml $value | default "{}" | indent 4 }}
+{{- end -}}
+{{- end -}}
diff --git a/manifests/prometheus/prometheus/templates/alertmanager-deployment.yaml b/manifests/prometheus/prometheus/templates/alertmanager-deployment.yaml
new file mode 100644
index 0000000..fdfe51f
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/alertmanager-deployment.yaml
@@ -0,0 +1,115 @@
+{{- if .Values.alertmanager.enabled -}}
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.alertmanager.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.alertmanager.fullname" . }}
+spec:
+ replicas: {{ .Values.alertmanager.replicaCount }}
+ {{- if .Values.server.strategy }}
+ strategy:
+{{ toYaml .Values.server.strategy | indent 4 }}
+ {{- end }}
+ template:
+ metadata:
+ {{- if .Values.alertmanager.podAnnotations }}
+ annotations:
+{{ toYaml .Values.alertmanager.podAnnotations | indent 8 }}
+ {{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.alertmanager.name }}"
+ release: {{ .Release.Name }}
+ spec:
+{{- if .Values.alertmanager.affinity }}
+ affinity:
+{{ toYaml .Values.alertmanager.affinity | indent 8 }}
+{{- end }}
+{{- if .Values.alertmanager.schedulerName }}
+ schedulerName: "{{ .Values.alertmanager.schedulerName }}"
+{{- end }}
+ serviceAccountName: {{ template "prometheus.serviceAccountName.alertmanager" . }}
+{{- if .Values.alertmanager.priorityClassName }}
+ priorityClassName: "{{ .Values.alertmanager.priorityClassName }}"
+{{- end }}
+ containers:
+ - name: {{ template "prometheus.name" . }}-{{ .Values.alertmanager.name }}
+ image: "{{ .Values.alertmanager.image.repository }}:{{ .Values.alertmanager.image.tag }}"
+ imagePullPolicy: "{{ .Values.alertmanager.image.pullPolicy }}"
+ env:
+ {{- range $key, $value := .Values.alertmanager.extraEnv }}
+ - name: {{ $key }}
+ value: {{ $value }}
+ {{- end }}
+ args:
+ - --config.file=/etc/config/alertmanager.yml
+ - --storage.path={{ .Values.alertmanager.persistentVolume.mountPath }}
+ {{- range $key, $value := .Values.alertmanager.extraArgs }}
+ - --{{ $key }}={{ $value }}
+ {{- end }}
+ {{- if .Values.alertmanager.baseURL }}
+ - --web.external-url={{ .Values.alertmanager.baseURL }}
+ {{- end }}
+
+ ports:
+ - containerPort: 9093
+ readinessProbe:
+ httpGet:
+ path: {{ .Values.alertmanager.prefixURL }}/#/status
+ port: 9093
+ initialDelaySeconds: 30
+ timeoutSeconds: 30
+ resources:
+{{ toYaml .Values.alertmanager.resources | indent 12 }}
+ volumeMounts:
+ - name: config-volume
+ mountPath: /etc/config
+ - name: storage-volume
+ mountPath: "{{ .Values.alertmanager.persistentVolume.mountPath }}"
+ subPath: "{{ .Values.alertmanager.persistentVolume.subPath }}"
+
+ - name: {{ template "prometheus.name" . }}-{{ .Values.alertmanager.name }}-{{ .Values.configmapReload.name }}
+ image: "{{ .Values.configmapReload.image.repository }}:{{ .Values.configmapReload.image.tag }}"
+ imagePullPolicy: "{{ .Values.configmapReload.image.pullPolicy }}"
+ args:
+ - --volume-dir=/etc/config
+ - --webhook-url=http://localhost:9093{{ .Values.alertmanager.prefixURL }}/-/reload
+ resources:
+{{ toYaml .Values.configmapReload.resources | indent 12 }}
+ volumeMounts:
+ - name: config-volume
+ mountPath: /etc/config
+ readOnly: true
+ {{- if .Values.alertmanager.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.alertmanager.nodeSelector | indent 8 }}
+ {{- end }}
+ {{- if .Values.alertmanager.securityContext }}
+ securityContext:
+{{ toYaml .Values.alertmanager.securityContext | indent 8 }}
+ {{- end }}
+ {{- if .Values.alertmanager.tolerations }}
+ tolerations:
+{{ toYaml .Values.alertmanager.tolerations | indent 8 }}
+ {{- end }}
+ {{- if .Values.alertmanager.affinity }}
+ affinity:
+{{ toYaml .Values.alertmanager.affinity | indent 8 }}
+ {{- end }}
+ volumes:
+ - name: config-volume
+ configMap:
+ name: {{ if .Values.alertmanager.configMapOverrideName }}{{ .Release.Name }}-{{ .Values.alertmanager.configMapOverrideName }}{{- else }}{{ template "prometheus.alertmanager.fullname" . }}{{- end }}
+ - name: storage-volume
+ {{- if .Values.alertmanager.persistentVolume.enabled }}
+ persistentVolumeClaim:
+ claimName: {{ if .Values.alertmanager.persistentVolume.existingClaim }}{{ .Values.alertmanager.persistentVolume.existingClaim }}{{- else }}{{ template "prometheus.alertmanager.fullname" . }}{{- end }}
+ {{- else }}
+ emptyDir: {}
+ {{- end -}}
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/alertmanager-ingress.yaml b/manifests/prometheus/prometheus/templates/alertmanager-ingress.yaml
new file mode 100644
index 0000000..41adae8
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/alertmanager-ingress.yaml
@@ -0,0 +1,38 @@
+{{- if and .Values.alertmanager.enabled .Values.alertmanager.ingress.enabled -}}
+{{- $releaseName := .Release.Name -}}
+{{- $serviceName := include "prometheus.alertmanager.fullname" . }}
+{{- $servicePort := .Values.alertmanager.service.servicePort -}}
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+{{- if .Values.alertmanager.ingress.annotations }}
+ annotations:
+{{ toYaml .Values.alertmanager.ingress.annotations | indent 4 }}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.alertmanager.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+{{- range $key, $value := .Values.alertmanager.ingress.extraLabels }}
+ {{ $key }}: {{ $value }}
+{{- end }}
+ name: {{ template "prometheus.alertmanager.fullname" . }}
+spec:
+ rules:
+ {{- range .Values.alertmanager.ingress.hosts }}
+ {{- $url := splitList "/" . }}
+ - host: {{ first $url }}
+ http:
+ paths:
+ - path: /{{ rest $url | join "/" }}
+ backend:
+ serviceName: {{ $serviceName }}
+ servicePort: {{ $servicePort }}
+ {{- end -}}
+{{- if .Values.alertmanager.ingress.tls }}
+ tls:
+{{ toYaml .Values.alertmanager.ingress.tls | indent 4 }}
+ {{- end -}}
+{{- end -}}
diff --git a/manifests/prometheus/prometheus/templates/alertmanager-networkpolicy.yaml b/manifests/prometheus/prometheus/templates/alertmanager-networkpolicy.yaml
new file mode 100644
index 0000000..9f5a7ca
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/alertmanager-networkpolicy.yaml
@@ -0,0 +1,26 @@
+{{- if .Values.networkPolicy.enabled }}
+apiVersion: {{ template "prometheus.networkPolicy.apiVersion" . }}
+kind: NetworkPolicy
+metadata:
+ name: {{ template "prometheus.alertmanager.fullname" . }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.alertmanager.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+spec:
+ podSelector:
+ matchLabels:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.alertmanager.name }}"
+ release: {{ .Release.Name }}
+ ingress:
+ - from:
+ - podSelector:
+ matchLabels:
+ release: {{ .Release.Name }}
+ component: "{{ .Values.server.name }}"
+ - ports:
+ - port: 9093
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/alertmanager-pvc.yaml b/manifests/prometheus/prometheus/templates/alertmanager-pvc.yaml
new file mode 100644
index 0000000..c93db4c
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/alertmanager-pvc.yaml
@@ -0,0 +1,31 @@
+{{- if and .Values.alertmanager.enabled .Values.alertmanager.persistentVolume.enabled -}}
+{{- if not .Values.alertmanager.persistentVolume.existingClaim -}}
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ {{- if .Values.alertmanager.persistentVolume.annotations }}
+ annotations:
+{{ toYaml .Values.alertmanager.persistentVolume.annotations | indent 4 }}
+ {{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.alertmanager.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.alertmanager.fullname" . }}
+spec:
+ accessModes:
+{{ toYaml .Values.alertmanager.persistentVolume.accessModes | indent 4 }}
+{{- if .Values.alertmanager.persistentVolume.storageClass }}
+{{- if (eq "-" .Values.alertmanager.persistentVolume.storageClass) }}
+ storageClassName: ""
+{{- else }}
+ storageClassName: "{{ .Values.alertmanager.persistentVolume.storageClass }}"
+{{- end }}
+{{- end }}
+ resources:
+ requests:
+ storage: "{{ .Values.alertmanager.persistentVolume.size }}"
+{{- end -}}
+{{- end -}}
diff --git a/manifests/prometheus/prometheus/templates/alertmanager-service.yaml b/manifests/prometheus/prometheus/templates/alertmanager-service.yaml
new file mode 100644
index 0000000..28f1be8
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/alertmanager-service.yaml
@@ -0,0 +1,55 @@
+{{- if .Values.alertmanager.enabled -}}
+apiVersion: v1
+kind: Service
+metadata:
+{{- if .Values.alertmanager.service.annotations }}
+ annotations:
+{{ toYaml .Values.alertmanager.service.annotations | indent 4 }}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.alertmanager.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+{{- if .Values.alertmanager.service.labels }}
+{{ toYaml .Values.alertmanager.service.labels | indent 4 }}
+{{- end }}
+ name: {{ template "prometheus.alertmanager.fullname" . }}
+spec:
+{{- if .Values.alertmanager.service.clusterIP }}
+ clusterIP: {{ .Values.alertmanager.service.clusterIP }}
+{{- end }}
+{{- if .Values.alertmanager.service.externalIPs }}
+ externalIPs:
+{{ toYaml .Values.alertmanager.service.externalIPs | indent 4 }}
+{{- end }}
+{{- if .Values.alertmanager.service.loadBalancerIP }}
+ loadBalancerIP: {{ .Values.alertmanager.service.loadBalancerIP }}
+{{- end }}
+{{- if .Values.alertmanager.service.loadBalancerSourceRanges }}
+ loadBalancerSourceRanges:
+ {{- range $cidr := .Values.alertmanager.service.loadBalancerSourceRanges }}
+ - {{ $cidr }}
+ {{- end }}
+{{- end }}
+ ports:
+ - name: http
+ port: {{ .Values.alertmanager.service.servicePort }}
+ protocol: TCP
+ targetPort: 9093
+ {{- if .Values.alertmanager.service.nodePort }}
+ nodePort: {{ .Values.alertmanager.service.nodePort }}
+ {{- end }}
+{{- if .Values.alertmanager.service.enableMeshPeer }}
+ - name: meshpeer
+ port: 6783
+ protocol: TCP
+ targetPort: 6783
+{{- end }}
+ selector:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.alertmanager.name }}"
+ release: {{ .Release.Name }}
+ type: "{{ .Values.alertmanager.service.type }}"
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/alertmanager-serviceaccount.yaml b/manifests/prometheus/prometheus/templates/alertmanager-serviceaccount.yaml
new file mode 100644
index 0000000..3194374
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/alertmanager-serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccounts.alertmanager.create }}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.alertmanager.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.serviceAccountName.alertmanager" . }}
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/kube-state-metrics-clusterrole.yaml b/manifests/prometheus/prometheus/templates/kube-state-metrics-clusterrole.yaml
new file mode 100644
index 0000000..86f46ef
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/kube-state-metrics-clusterrole.yaml
@@ -0,0 +1,64 @@
+{{- if .Values.rbac.create }}
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.kubeStateMetrics.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.kubeStateMetrics.fullname" . }}
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - namespaces
+ - nodes
+ - persistentvolumeclaims
+ - pods
+ - services
+ - resourcequotas
+ - replicationcontrollers
+ - limitranges
+ - persistentvolumeclaims
+ - persistentvolumes
+ - endpoints
+ - secrets
+ - configmaps
+ verbs:
+ - list
+ - watch
+ - apiGroups:
+ - extensions
+ resources:
+ - daemonsets
+ - deployments
+ - replicasets
+ verbs:
+ - list
+ - watch
+ - apiGroups:
+ - apps
+ resources:
+ - statefulsets
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - batch
+ resources:
+ - cronjobs
+ - jobs
+ verbs:
+ - list
+ - watch
+ - apiGroups:
+ - autoscaling
+ resources:
+ - horizontalpodautoscalers
+ verbs:
+ - list
+ - watch
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/kube-state-metrics-clusterrolebinding.yaml b/manifests/prometheus/prometheus/templates/kube-state-metrics-clusterrolebinding.yaml
new file mode 100644
index 0000000..00bf8d8
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/kube-state-metrics-clusterrolebinding.yaml
@@ -0,0 +1,20 @@
+{{- if .Values.rbac.create }}
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.kubeStateMetrics.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.kubeStateMetrics.fullname" . }}
+subjects:
+ - kind: ServiceAccount
+ name: {{ template "prometheus.serviceAccountName.kubeStateMetrics" . }}
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: {{ template "prometheus.kubeStateMetrics.fullname" . }}
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/kube-state-metrics-deployment.yaml b/manifests/prometheus/prometheus/templates/kube-state-metrics-deployment.yaml
new file mode 100644
index 0000000..3ff498f
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/kube-state-metrics-deployment.yaml
@@ -0,0 +1,67 @@
+{{- if .Values.kubeStateMetrics.enabled -}}
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+{{- if .Values.kubeStateMetrics.deploymentAnnotations }}
+ annotations:
+{{ toYaml .Values.kubeStateMetrics.deploymentAnnotations | indent 4 }}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.kubeStateMetrics.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.kubeStateMetrics.fullname" . }}
+spec:
+ replicas: {{ .Values.kubeStateMetrics.replicaCount }}
+ template:
+ metadata:
+ {{- if .Values.kubeStateMetrics.podAnnotations }}
+ annotations:
+{{ toYaml .Values.kubeStateMetrics.podAnnotations | indent 8 }}
+ {{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.kubeStateMetrics.name }}"
+ release: {{ .Release.Name }}
+{{- if .Values.kubeStateMetrics.pod.labels }}
+{{ toYaml .Values.kubeStateMetrics.pod.labels | indent 8 }}
+{{- end }}
+ spec:
+ serviceAccountName: {{ template "prometheus.serviceAccountName.kubeStateMetrics" . }}
+{{- if .Values.kubeStateMetrics.priorityClassName }}
+ priorityClassName: "{{ .Values.kubeStateMetrics.priorityClassName }}"
+{{- end }}
+ containers:
+ - name: {{ template "prometheus.name" . }}-{{ .Values.kubeStateMetrics.name }}
+ image: "{{ .Values.kubeStateMetrics.image.repository }}:{{ .Values.kubeStateMetrics.image.tag }}"
+ imagePullPolicy: "{{ .Values.kubeStateMetrics.image.pullPolicy }}"
+ {{- if .Values.kubeStateMetrics.args }}
+ args:
+ {{- range $key, $value := .Values.kubeStateMetrics.args }}
+ - --{{ $key }}={{ $value }}
+ {{- end }}
+ {{- end }}
+ ports:
+ - name: metrics
+ containerPort: 8080
+ resources:
+{{ toYaml .Values.kubeStateMetrics.resources | indent 12 }}
+ {{- if .Values.kubeStateMetrics.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.kubeStateMetrics.nodeSelector | indent 8 }}
+ {{- end }}
+ {{- if .Values.kubeStateMetrics.securityContext }}
+ securityContext:
+{{ toYaml .Values.kubeStateMetrics.securityContext | indent 8 }}
+ {{- end }}
+ {{- if .Values.kubeStateMetrics.tolerations }}
+ tolerations:
+{{ toYaml .Values.kubeStateMetrics.tolerations | indent 8 }}
+ {{- end }}
+ {{- if .Values.kubeStateMetrics.affinity }}
+ affinity:
+{{ toYaml .Values.kubeStateMetrics.affinity | indent 8 }}
+ {{- end }}
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/kube-state-metrics-networkpolicy.yaml b/manifests/prometheus/prometheus/templates/kube-state-metrics-networkpolicy.yaml
new file mode 100644
index 0000000..3fc62e2
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/kube-state-metrics-networkpolicy.yaml
@@ -0,0 +1,26 @@
+{{- if .Values.networkPolicy.enabled }}
+apiVersion: {{ template "prometheus.networkPolicy.apiVersion" . }}
+kind: NetworkPolicy
+metadata:
+ name: {{ template "prometheus.kubeStateMetrics.fullname" . }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.kubeStateMetrics.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+spec:
+ podSelector:
+ matchLabels:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.kubeStateMetrics.name }}"
+ release: {{ .Release.Name }}
+ ingress:
+ - from:
+ - podSelector:
+ matchLabels:
+ release: {{ .Release.Name }}
+ component: "{{ .Values.server.name }}"
+ - ports:
+ - port: 8080
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/kube-state-metrics-serviceaccount.yaml b/manifests/prometheus/prometheus/templates/kube-state-metrics-serviceaccount.yaml
new file mode 100644
index 0000000..871a875
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/kube-state-metrics-serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccounts.kubeStateMetrics.create }}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.kubeStateMetrics.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.serviceAccountName.kubeStateMetrics" . }}
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/kube-state-metrics-svc.yaml b/manifests/prometheus/prometheus/templates/kube-state-metrics-svc.yaml
new file mode 100644
index 0000000..e11fa06
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/kube-state-metrics-svc.yaml
@@ -0,0 +1,46 @@
+{{- if .Values.kubeStateMetrics.enabled -}}
+apiVersion: v1
+kind: Service
+metadata:
+{{- if .Values.kubeStateMetrics.service.annotations }}
+ annotations:
+{{ toYaml .Values.kubeStateMetrics.service.annotations | indent 4 }}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.kubeStateMetrics.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+{{- if .Values.kubeStateMetrics.service.labels }}
+{{ toYaml .Values.kubeStateMetrics.service.labels | indent 4 }}
+{{- end }}
+ name: {{ template "prometheus.kubeStateMetrics.fullname" . }}
+spec:
+{{- if .Values.kubeStateMetrics.service.clusterIP }}
+ clusterIP: {{ .Values.kubeStateMetrics.service.clusterIP }}
+{{- end }}
+{{- if .Values.kubeStateMetrics.service.externalIPs }}
+ externalIPs:
+{{ toYaml .Values.kubeStateMetrics.service.externalIPs | indent 4 }}
+{{- end }}
+{{- if .Values.kubeStateMetrics.service.loadBalancerIP }}
+ loadBalancerIP: {{ .Values.kubeStateMetrics.service.loadBalancerIP }}
+{{- end }}
+{{- if .Values.kubeStateMetrics.service.loadBalancerSourceRanges }}
+ loadBalancerSourceRanges:
+ {{- range $cidr := .Values.kubeStateMetrics.service.loadBalancerSourceRanges }}
+ - {{ $cidr }}
+ {{- end }}
+{{- end }}
+ ports:
+ - name: http
+ port: {{ .Values.kubeStateMetrics.service.servicePort }}
+ protocol: TCP
+ targetPort: 8080
+ selector:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.kubeStateMetrics.name }}"
+ release: {{ .Release.Name }}
+ type: "{{ .Values.kubeStateMetrics.service.type }}"
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/node-exporter-daemonset.yaml b/manifests/prometheus/prometheus/templates/node-exporter-daemonset.yaml
new file mode 100644
index 0000000..96fae95
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/node-exporter-daemonset.yaml
@@ -0,0 +1,112 @@
+{{- if .Values.nodeExporter.enabled -}}
+apiVersion: extensions/v1beta1
+kind: DaemonSet
+metadata:
+{{- if .Values.nodeExporter.deploymentAnnotations }}
+ annotations:
+{{ toYaml .Values.nodeExporter.deploymentAnnotations | indent 4 }}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.nodeExporter.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.nodeExporter.fullname" . }}
+spec:
+ {{- if .Values.nodeExporter.updateStrategy }}
+ updateStrategy:
+{{ toYaml .Values.nodeExporter.updateStrategy | indent 4 }}
+ {{- end }}
+ template:
+ metadata:
+ {{- if .Values.nodeExporter.podAnnotations }}
+ annotations:
+{{ toYaml .Values.nodeExporter.podAnnotations | indent 8 }}
+ {{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.nodeExporter.name }}"
+ release: {{ .Release.Name }}
+{{- if .Values.nodeExporter.pod.labels }}
+{{ toYaml .Values.nodeExporter.pod.labels | indent 8 }}
+{{- end }}
+ spec:
+ serviceAccountName: {{ template "prometheus.serviceAccountName.nodeExporter" . }}
+{{- if .Values.nodeExporter.priorityClassName }}
+ priorityClassName: "{{ .Values.nodeExporter.priorityClassName }}"
+{{- end }}
+ containers:
+ - name: {{ template "prometheus.name" . }}-{{ .Values.nodeExporter.name }}
+ image: "{{ .Values.nodeExporter.image.repository }}:{{ .Values.nodeExporter.image.tag }}"
+ imagePullPolicy: "{{ .Values.nodeExporter.image.pullPolicy }}"
+ args:
+ - --path.procfs=/host/proc
+ - --path.sysfs=/host/sys
+ {{- range $key, $value := .Values.nodeExporter.extraArgs }}
+ {{- if $value }}
+ - --{{ $key }}={{ $value }}
+ {{- else }}
+ - --{{ $key }}
+ {{- end }}
+ {{- end }}
+ ports:
+ - name: metrics
+ containerPort: 9100
+ hostPort: {{ .Values.nodeExporter.service.hostPort }}
+ resources:
+{{ toYaml .Values.nodeExporter.resources | indent 12 }}
+ volumeMounts:
+ - name: proc
+ mountPath: /host/proc
+ readOnly: true
+ - name: sys
+ mountPath: /host/sys
+ readOnly: true
+ {{- range .Values.nodeExporter.extraHostPathMounts }}
+ - name: {{ .name }}
+ mountPath: {{ .mountPath }}
+ readOnly: {{ .readOnly }}
+ {{- end }}
+ {{- range .Values.nodeExporter.extraConfigmapMounts }}
+ - name: {{ .name }}
+ mountPath: {{ .mountPath }}
+ readOnly: {{ .readOnly }}
+ {{- end }}
+ {{- if .Values.nodeExporter.hostNetwork }}
+ hostNetwork: true
+ {{- end }}
+ {{- if .Values.nodeExporter.hostPID }}
+ hostPID: true
+ {{- end }}
+ {{- if .Values.nodeExporter.tolerations }}
+ tolerations:
+{{ toYaml .Values.nodeExporter.tolerations | indent 8 }}
+ {{- end }}
+ {{- if .Values.nodeExporter.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.nodeExporter.nodeSelector | indent 8 }}
+ {{- end }}
+ {{- if .Values.nodeExporter.securityContext }}
+ securityContext:
+{{ toYaml .Values.nodeExporter.securityContext | indent 8 }}
+ {{- end }}
+ volumes:
+ - name: proc
+ hostPath:
+ path: /proc
+ - name: sys
+ hostPath:
+ path: /sys
+ {{- range .Values.nodeExporter.extraHostPathMounts }}
+ - name: {{ .name }}
+ hostPath:
+ path: {{ .hostPath }}
+ {{- end }}
+ {{- range .Values.nodeExporter.extraConfigmapMounts }}
+ - name: {{ .name }}
+ configMap:
+ name: {{ .configMap }}
+ {{- end }}
+
+{{- end -}}
diff --git a/manifests/prometheus/prometheus/templates/node-exporter-service.yaml b/manifests/prometheus/prometheus/templates/node-exporter-service.yaml
new file mode 100644
index 0000000..6af14c2
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/node-exporter-service.yaml
@@ -0,0 +1,46 @@
+{{- if .Values.nodeExporter.enabled -}}
+apiVersion: v1
+kind: Service
+metadata:
+{{- if .Values.nodeExporter.service.annotations }}
+ annotations:
+{{ toYaml .Values.nodeExporter.service.annotations | indent 4 }}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.nodeExporter.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+{{- if .Values.nodeExporter.service.labels }}
+{{ toYaml .Values.nodeExporter.service.labels | indent 4 }}
+{{- end }}
+ name: {{ template "prometheus.nodeExporter.fullname" . }}
+spec:
+{{- if .Values.nodeExporter.service.clusterIP }}
+ clusterIP: {{ .Values.nodeExporter.service.clusterIP }}
+{{- end }}
+{{- if .Values.nodeExporter.service.externalIPs }}
+ externalIPs:
+{{ toYaml .Values.nodeExporter.service.externalIPs | indent 4 }}
+{{- end }}
+{{- if .Values.nodeExporter.service.loadBalancerIP }}
+ loadBalancerIP: {{ .Values.nodeExporter.service.loadBalancerIP }}
+{{- end }}
+{{- if .Values.nodeExporter.service.loadBalancerSourceRanges }}
+ loadBalancerSourceRanges:
+ {{- range $cidr := .Values.nodeExporter.service.loadBalancerSourceRanges }}
+ - {{ $cidr }}
+ {{- end }}
+{{- end }}
+ ports:
+ - name: metrics
+ port: {{ .Values.nodeExporter.service.servicePort }}
+ protocol: TCP
+ targetPort: 9100
+ selector:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.nodeExporter.name }}"
+ release: {{ .Release.Name }}
+ type: "{{ .Values.nodeExporter.service.type }}"
+{{- end -}}
diff --git a/manifests/prometheus/prometheus/templates/node-exporter-serviceaccount.yaml b/manifests/prometheus/prometheus/templates/node-exporter-serviceaccount.yaml
new file mode 100644
index 0000000..da45088
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/node-exporter-serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccounts.nodeExporter.create }}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.nodeExporter.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.serviceAccountName.nodeExporter" . }}
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/pushgateway-deployment.yaml b/manifests/prometheus/prometheus/templates/pushgateway-deployment.yaml
new file mode 100644
index 0000000..13d46e9
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/pushgateway-deployment.yaml
@@ -0,0 +1,67 @@
+{{- if .Values.pushgateway.enabled -}}
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.pushgateway.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.pushgateway.fullname" . }}
+spec:
+ replicas: {{ .Values.pushgateway.replicaCount }}
+ template:
+ metadata:
+ {{- if .Values.pushgateway.podAnnotations }}
+ annotations:
+{{ toYaml .Values.pushgateway.podAnnotations | indent 8 }}
+ {{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.pushgateway.name }}"
+ release: {{ .Release.Name }}
+ spec:
+ serviceAccountName: {{ template "prometheus.serviceAccountName.pushgateway" . }}
+{{- if .Values.pushgateway.priorityClassName }}
+ priorityClassName: "{{ .Values.pushgateway.priorityClassName }}"
+{{- end }}
+ containers:
+ - name: {{ template "prometheus.name" . }}-{{ .Values.pushgateway.name }}
+ image: "{{ .Values.pushgateway.image.repository }}:{{ .Values.pushgateway.image.tag }}"
+ imagePullPolicy: "{{ .Values.pushgateway.image.pullPolicy }}"
+ args:
+ {{- range $key, $value := .Values.pushgateway.extraArgs }}
+ - --{{ $key }}={{ $value }}
+ {{- end }}
+ ports:
+ - containerPort: 9091
+ readinessProbe:
+ httpGet:
+ {{- if (index .Values "pushgateway" "extraArgs" "web.route-prefix") }}
+ path: /{{ index .Values "pushgateway" "extraArgs" "web.route-prefix" }}/#/status
+ {{- else }}
+ path: /#/status
+ {{- end }}
+ port: 9091
+ initialDelaySeconds: 10
+ timeoutSeconds: 10
+ resources:
+{{ toYaml .Values.pushgateway.resources | indent 12 }}
+ {{- if .Values.pushgateway.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.pushgateway.nodeSelector | indent 8 }}
+ {{- end }}
+ {{- if .Values.pushgateway.securityContext }}
+ securityContext:
+{{ toYaml .Values.pushgateway.securityContext | indent 8 }}
+ {{- end }}
+ {{- if .Values.pushgateway.tolerations }}
+ tolerations:
+{{ toYaml .Values.pushgateway.tolerations | indent 8 }}
+ {{- end }}
+ {{- if .Values.pushgateway.affinity }}
+ affinity:
+{{ toYaml .Values.pushgateway.affinity | indent 8 }}
+ {{- end }}
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/pushgateway-ingress.yaml b/manifests/prometheus/prometheus/templates/pushgateway-ingress.yaml
new file mode 100644
index 0000000..e0538fd
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/pushgateway-ingress.yaml
@@ -0,0 +1,35 @@
+{{- if and .Values.pushgateway.enabled .Values.pushgateway.ingress.enabled -}}
+{{- $releaseName := .Release.Name -}}
+{{- $serviceName := include "prometheus.pushgateway.fullname" . }}
+{{- $servicePort := .Values.pushgateway.service.servicePort -}}
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+{{- if .Values.pushgateway.ingress.annotations }}
+ annotations:
+{{ toYaml .Values.pushgateway.ingress.annotations | indent 4}}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.pushgateway.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.pushgateway.fullname" . }}
+spec:
+ rules:
+ {{- range .Values.pushgateway.ingress.hosts }}
+ {{- $url := splitList "/" . }}
+ - host: {{ first $url }}
+ http:
+ paths:
+ - path: /{{ rest $url | join "/" }}
+ backend:
+ serviceName: {{ $serviceName }}
+ servicePort: {{ $servicePort }}
+ {{- end -}}
+{{- if .Values.pushgateway.ingress.tls }}
+ tls:
+{{ toYaml .Values.pushgateway.ingress.tls | indent 4 }}
+ {{- end -}}
+{{- end -}}
diff --git a/manifests/prometheus/prometheus/templates/pushgateway-service.yaml b/manifests/prometheus/prometheus/templates/pushgateway-service.yaml
new file mode 100644
index 0000000..9686319
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/pushgateway-service.yaml
@@ -0,0 +1,46 @@
+{{- if .Values.pushgateway.enabled -}}
+apiVersion: v1
+kind: Service
+metadata:
+{{- if .Values.pushgateway.service.annotations }}
+ annotations:
+{{ toYaml .Values.pushgateway.service.annotations | indent 4}}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.pushgateway.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+{{- if .Values.pushgateway.service.labels }}
+{{ toYaml .Values.pushgateway.service.labels | indent 4}}
+{{- end }}
+ name: {{ template "prometheus.pushgateway.fullname" . }}
+spec:
+{{- if .Values.pushgateway.service.clusterIP }}
+ clusterIP: {{ .Values.pushgateway.service.clusterIP }}
+{{- end }}
+{{- if .Values.pushgateway.service.externalIPs }}
+ externalIPs:
+{{ toYaml .Values.pushgateway.service.externalIPs | indent 4 }}
+{{- end }}
+{{- if .Values.pushgateway.service.loadBalancerIP }}
+ loadBalancerIP: {{ .Values.pushgateway.service.loadBalancerIP }}
+{{- end }}
+{{- if .Values.pushgateway.service.loadBalancerSourceRanges }}
+ loadBalancerSourceRanges:
+ {{- range $cidr := .Values.pushgateway.service.loadBalancerSourceRanges }}
+ - {{ $cidr }}
+ {{- end }}
+{{- end }}
+ ports:
+ - name: http
+ port: {{ .Values.pushgateway.service.servicePort }}
+ protocol: TCP
+ targetPort: 9091
+ selector:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.pushgateway.name }}"
+ release: {{ .Release.Name }}
+ type: "{{ .Values.pushgateway.service.type }}"
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/pushgateway-serviceaccount.yaml b/manifests/prometheus/prometheus/templates/pushgateway-serviceaccount.yaml
new file mode 100644
index 0000000..dec1377
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/pushgateway-serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccounts.pushgateway.create }}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.pushgateway.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.serviceAccountName.pushgateway" . }}
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/server-clusterrole.yaml b/manifests/prometheus/prometheus/templates/server-clusterrole.yaml
new file mode 100644
index 0000000..2e56996
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/server-clusterrole.yaml
@@ -0,0 +1,45 @@
+{{- if .Values.rbac.create }}
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.server.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.server.fullname" . }}
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - nodes
+ - nodes/proxy
+ - services
+ - endpoints
+ - pods
+ - ingresses
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - apiGroups:
+ - "extensions"
+ resources:
+ - ingresses/status
+ - ingresses
+ verbs:
+ - get
+ - list
+ - watch
+ - nonResourceURLs:
+ - "/metrics"
+ verbs:
+ - get
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/server-clusterrolebinding.yaml b/manifests/prometheus/prometheus/templates/server-clusterrolebinding.yaml
new file mode 100644
index 0000000..776e869
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/server-clusterrolebinding.yaml
@@ -0,0 +1,20 @@
+{{- if .Values.rbac.create }}
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.server.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.server.fullname" . }}
+subjects:
+ - kind: ServiceAccount
+ name: {{ template "prometheus.serviceAccountName.server" . }}
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: {{ template "prometheus.server.fullname" . }}
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/server-configmap.yaml b/manifests/prometheus/prometheus/templates/server-configmap.yaml
new file mode 100644
index 0000000..65f3590
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/server-configmap.yaml
@@ -0,0 +1,49 @@
+{{- if (empty .Values.server.configMapOverrideName) -}}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.server.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.server.fullname" . }}
+data:
+{{- $root := . -}}
+{{- range $key, $value := .Values.serverFiles }}
+ {{ $key }}: |
+{{- if eq $key "prometheus.yml" }}
+ global:
+{{ $root.Values.server.global | toYaml | indent 6 }}
+{{- end }}
+{{ toYaml $value | default "{}" | indent 4 }}
+{{- if eq $key "prometheus.yml" -}}
+{{- if $root.Values.alertmanager.enabled }}
+ alerting:
+ alertmanagers:
+ - kubernetes_sd_configs:
+ - role: pod
+ tls_config:
+ ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
+ bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
+ {{- if $root.Values.alertmanager.prefixURL }}
+ path_prefix: {{ $root.Values.alertmanager.prefixURL }}
+ {{- end }}
+ relabel_configs:
+ - source_labels: [__meta_kubernetes_namespace]
+ regex: {{ $root.Release.Namespace }}
+ action: keep
+ - source_labels: [__meta_kubernetes_pod_label_app]
+ regex: {{ template "prometheus.name" $root }}
+ action: keep
+ - source_labels: [__meta_kubernetes_pod_label_component]
+ regex: alertmanager
+ action: keep
+ - source_labels: [__meta_kubernetes_pod_container_port_number]
+ regex:
+ action: drop
+{{- end -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
diff --git a/manifests/prometheus/prometheus/templates/server-deployment.yaml b/manifests/prometheus/prometheus/templates/server-deployment.yaml
new file mode 100644
index 0000000..418d703
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/server-deployment.yaml
@@ -0,0 +1,189 @@
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+{{- if .Values.server.deploymentAnnotations }}
+ annotations:
+{{ toYaml .Values.server.deploymentAnnotations | indent 4 }}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.server.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.server.fullname" . }}
+spec:
+ replicas: {{ .Values.server.replicaCount }}
+ {{- if .Values.server.strategy }}
+ strategy:
+{{ toYaml .Values.server.strategy | indent 4 }}
+ {{- end }}
+ template:
+ metadata:
+ {{- if .Values.server.podAnnotations }}
+ annotations:
+{{ toYaml .Values.server.podAnnotations | indent 8 }}
+ {{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.server.name }}"
+ release: {{ .Release.Name }}
+ spec:
+{{- if .Values.server.affinity }}
+ affinity:
+{{ toYaml .Values.server.affinity | indent 8 }}
+{{- end }}
+{{- if .Values.server.priorityClassName }}
+ priorityClassName: "{{ .Values.server.priorityClassName }}"
+{{- end }}
+{{- if .Values.server.schedulerName }}
+ schedulerName: "{{ .Values.server.schedulerName }}"
+{{- end }}
+ serviceAccountName: {{ template "prometheus.serviceAccountName.server" . }}
+ {{- if .Values.initChownData.enabled }}
+ initContainers:
+ - name: "{{ .Values.initChownData.name }}"
+ image: "{{ .Values.initChownData.image.repository }}:{{ .Values.initChownData.image.tag }}"
+ imagePullPolicy: "{{ .Values.initChownData.image.pullPolicy }}"
+ resources:
+{{ toYaml .Values.initChownData.resources | indent 12 }}
+ # 65534 is the nobody user that prometheus uses.
+ command: ["chown", "-R", "65534:65534", "{{ .Values.server.persistentVolume.mountPath }}"]
+ volumeMounts:
+ - name: storage-volume
+ mountPath: {{ .Values.server.persistentVolume.mountPath }}
+ subPath: "{{ .Values.server.persistentVolume.subPath }}"
+ {{- end }}
+ containers:
+ - name: {{ template "prometheus.name" . }}-{{ .Values.server.name }}-{{ .Values.configmapReload.name }}
+ image: "{{ .Values.configmapReload.image.repository }}:{{ .Values.configmapReload.image.tag }}"
+ imagePullPolicy: "{{ .Values.configmapReload.image.pullPolicy }}"
+ args:
+ - --volume-dir=/etc/config
+ - --webhook-url=http://127.0.0.1:9090{{ .Values.server.prefixURL }}/-/reload
+ {{- range $key, $value := .Values.configmapReload.extraArgs }}
+ - --{{ $key }}={{ $value }}
+ {{- end }}
+ resources:
+{{ toYaml .Values.configmapReload.resources | indent 12 }}
+ volumeMounts:
+ - name: config-volume
+ mountPath: /etc/config
+ readOnly: true
+ {{- range .Values.configmapReload.extraConfigmapMounts }}
+ - name: {{ $.Values.configmapReload.name }}-{{ .name }}
+ mountPath: {{ .mountPath }}
+ readOnly: {{ .readOnly }}
+ {{- end }}
+
+ - name: {{ template "prometheus.name" . }}-{{ .Values.server.name }}
+ image: "{{ .Values.server.image.repository }}:{{ .Values.server.image.tag }}"
+ imagePullPolicy: "{{ .Values.server.image.pullPolicy }}"
+ args:
+ {{- if .Values.server.retention }}
+ - --storage.tsdb.retention={{ .Values.server.retention }}
+ {{- end }}
+ - --config.file=/etc/config/prometheus.yml
+ - --storage.tsdb.path={{ .Values.server.persistentVolume.mountPath }}
+ - --web.console.libraries=/etc/prometheus/console_libraries
+ - --web.console.templates=/etc/prometheus/consoles
+ - --web.enable-lifecycle
+ {{- range $key, $value := .Values.server.extraArgs }}
+ - --{{ $key }}={{ $value }}
+ {{- end }}
+ {{- if .Values.server.baseURL }}
+ - --web.external-url={{ .Values.server.baseURL }}
+ {{- end }}
+ {{- if .Values.server.enableAdminApi }}
+ - --web.enable-admin-api
+ {{- end }}
+ ports:
+ - containerPort: 9090
+ readinessProbe:
+ httpGet:
+ path: {{ .Values.server.prefixURL }}/-/ready
+ port: 9090
+ initialDelaySeconds: 30
+ timeoutSeconds: 30
+ livenessProbe:
+ httpGet:
+ path: {{ .Values.server.prefixURL }}/-/healthy
+ port: 9090
+ initialDelaySeconds: 30
+ timeoutSeconds: 30
+ resources:
+{{ toYaml .Values.server.resources | indent 12 }}
+ volumeMounts:
+ - name: config-volume
+ mountPath: /etc/config
+ - name: storage-volume
+ mountPath: {{ .Values.server.persistentVolume.mountPath }}
+ subPath: "{{ .Values.server.persistentVolume.subPath }}"
+ {{- range .Values.server.extraHostPathMounts }}
+ - name: {{ .name }}
+ mountPath: {{ .mountPath }}
+ readOnly: {{ .readOnly }}
+ {{- end }}
+ {{- range .Values.server.extraConfigmapMounts }}
+ - name: {{ $.Values.server.name }}-{{ .name }}
+ mountPath: {{ .mountPath }}
+ readOnly: {{ .readOnly }}
+ {{- end }}
+ {{- range .Values.server.extraSecretMounts }}
+ - name: {{ .name }}
+ mountPath: {{ .mountPath }}
+ readOnly: {{ .readOnly }}
+ {{- end }}
+ {{- if .Values.server.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.server.nodeSelector | indent 8 }}
+ {{- end }}
+ {{- if .Values.server.securityContext }}
+ securityContext:
+{{ toYaml .Values.server.securityContext | indent 8 }}
+ {{- end }}
+ {{- if .Values.server.tolerations }}
+ tolerations:
+{{ toYaml .Values.server.tolerations | indent 8 }}
+ {{- end }}
+ {{- if .Values.server.affinity }}
+ affinity:
+{{ toYaml .Values.server.affinity | indent 8 }}
+ {{- end }}
+ terminationGracePeriodSeconds: {{ .Values.server.terminationGracePeriodSeconds }}
+ volumes:
+ - name: config-volume
+ configMap:
+ name: {{ if .Values.server.configMapOverrideName }}{{ .Release.Name }}-{{ .Values.server.configMapOverrideName }}{{- else }}{{ template "prometheus.server.fullname" . }}{{- end }}
+ - name: storage-volume
+ {{- if .Values.server.persistentVolume.enabled }}
+ persistentVolumeClaim:
+ claimName: {{ if .Values.server.persistentVolume.existingClaim }}{{ .Values.server.persistentVolume.existingClaim }}{{- else }}{{ template "prometheus.server.fullname" . }}{{- end }}
+ {{- else }}
+ emptyDir: {}
+ {{- end -}}
+ {{- range .Values.server.extraHostPathMounts }}
+ - name: {{ .name }}
+ hostPath:
+ path: {{ .hostPath }}
+ {{- end }}
+ {{- range .Values.configmapReload.extraConfigmapMounts }}
+ - name: {{ $.Values.configmapReload.name }}-{{ .name }}
+ configMap:
+ name: {{ .configMap }}
+ {{- end }}
+ {{- range .Values.server.extraConfigmapMounts }}
+ - name: {{ $.Values.server.name }}-{{ .name }}
+ configMap:
+ name: {{ .configMap }}
+ {{- end }}
+ {{- range .Values.server.extraSecretMounts }}
+ - name: {{ .name }}
+ secret:
+ secretName: {{ .secretName }}
+ {{- end }}
+ {{- range .Values.configmapReload.extraConfigmapMounts }}
+ - name: {{ .name }}
+ configMap:
+ name: {{ .configMap }}
+ {{- end }}
diff --git a/manifests/prometheus/prometheus/templates/server-ingress.yaml b/manifests/prometheus/prometheus/templates/server-ingress.yaml
new file mode 100644
index 0000000..db828ab
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/server-ingress.yaml
@@ -0,0 +1,38 @@
+{{- if .Values.server.ingress.enabled -}}
+{{- $releaseName := .Release.Name -}}
+{{- $serviceName := include "prometheus.server.fullname" . }}
+{{- $servicePort := .Values.server.service.servicePort -}}
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+{{- if .Values.server.ingress.annotations }}
+ annotations:
+{{ toYaml .Values.server.ingress.annotations | indent 4 }}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.server.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+{{- range $key, $value := .Values.server.ingress.extraLabels }}
+ {{ $key }}: {{ $value }}
+{{- end }}
+ name: {{ template "prometheus.server.fullname" . }}
+spec:
+ rules:
+ {{- range .Values.server.ingress.hosts }}
+ {{- $url := splitList "/" . }}
+ - host: {{ first $url }}
+ http:
+ paths:
+ - path: /{{ rest $url | join "/" }}
+ backend:
+ serviceName: {{ $serviceName }}
+ servicePort: {{ $servicePort }}
+ {{- end -}}
+{{- if .Values.server.ingress.tls }}
+ tls:
+{{ toYaml .Values.server.ingress.tls | indent 4 }}
+ {{- end -}}
+{{- end -}}
diff --git a/manifests/prometheus/prometheus/templates/server-networkpolicy.yaml b/manifests/prometheus/prometheus/templates/server-networkpolicy.yaml
new file mode 100644
index 0000000..3f1cdcc
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/server-networkpolicy.yaml
@@ -0,0 +1,21 @@
+{{- if .Values.networkPolicy.enabled }}
+apiVersion: {{ template "prometheus.networkPolicy.apiVersion" . }}
+kind: NetworkPolicy
+metadata:
+ name: {{ template "prometheus.server.fullname" . }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.server.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+spec:
+ podSelector:
+ matchLabels:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.server.name }}"
+ release: {{ .Release.Name }}
+ ingress:
+ - ports:
+ - port: 9090
+{{- end }}
diff --git a/manifests/prometheus/prometheus/templates/server-pvc.yaml b/manifests/prometheus/prometheus/templates/server-pvc.yaml
new file mode 100644
index 0000000..c4711e9
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/server-pvc.yaml
@@ -0,0 +1,31 @@
+{{- if .Values.server.persistentVolume.enabled -}}
+{{- if not .Values.server.persistentVolume.existingClaim -}}
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ {{- if .Values.server.persistentVolume.annotations }}
+ annotations:
+{{ toYaml .Values.server.persistentVolume.annotations | indent 4 }}
+ {{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.server.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.server.fullname" . }}
+spec:
+ accessModes:
+{{ toYaml .Values.server.persistentVolume.accessModes | indent 4 }}
+{{- if .Values.server.persistentVolume.storageClass }}
+{{- if (eq "-" .Values.server.persistentVolume.storageClass) }}
+ storageClassName: ""
+{{- else }}
+ storageClassName: "{{ .Values.server.persistentVolume.storageClass }}"
+{{- end }}
+{{- end }}
+ resources:
+ requests:
+ storage: "{{ .Values.server.persistentVolume.size }}"
+{{- end -}}
+{{- end -}}
diff --git a/manifests/prometheus/prometheus/templates/server-service.yaml b/manifests/prometheus/prometheus/templates/server-service.yaml
new file mode 100644
index 0000000..be81694
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/server-service.yaml
@@ -0,0 +1,47 @@
+apiVersion: v1
+kind: Service
+metadata:
+{{- if .Values.server.service.annotations }}
+ annotations:
+{{ toYaml .Values.server.service.annotations | indent 4 }}
+{{- end }}
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.server.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+{{- if .Values.server.service.labels }}
+{{ toYaml .Values.server.service.labels | indent 4 }}
+{{- end }}
+ name: {{ template "prometheus.server.fullname" . }}
+spec:
+{{- if .Values.server.service.clusterIP }}
+ clusterIP: {{ .Values.server.service.clusterIP }}
+{{- end }}
+{{- if .Values.server.service.externalIPs }}
+ externalIPs:
+{{ toYaml .Values.server.service.externalIPs | indent 4 }}
+{{- end }}
+{{- if .Values.server.service.loadBalancerIP }}
+ loadBalancerIP: {{ .Values.server.service.loadBalancerIP }}
+{{- end }}
+{{- if .Values.server.service.loadBalancerSourceRanges }}
+ loadBalancerSourceRanges:
+ {{- range $cidr := .Values.server.service.loadBalancerSourceRanges }}
+ - {{ $cidr }}
+ {{- end }}
+{{- end }}
+ ports:
+ - name: http
+ port: {{ .Values.server.service.servicePort }}
+ protocol: TCP
+ targetPort: 9090
+ {{- if .Values.server.service.nodePort }}
+ nodePort: {{ .Values.server.service.nodePort }}
+ {{- end }}
+ selector:
+ app: {{ template "prometheus.name" . }}
+ component: "{{ .Values.server.name }}"
+ release: {{ .Release.Name }}
+ type: "{{ .Values.server.service.type }}"
diff --git a/manifests/prometheus/prometheus/templates/server-serviceaccount.yaml b/manifests/prometheus/prometheus/templates/server-serviceaccount.yaml
new file mode 100644
index 0000000..c688ee4
--- /dev/null
+++ b/manifests/prometheus/prometheus/templates/server-serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccounts.server.create }}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app: {{ template "prometheus.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ component: "{{ .Values.server.name }}"
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ name: {{ template "prometheus.serviceAccountName.server" . }}
+{{- end }}
diff --git a/manifests/prometheus/prometheus/values.yaml b/manifests/prometheus/prometheus/values.yaml
new file mode 100644
index 0000000..8a286b1
--- /dev/null
+++ b/manifests/prometheus/prometheus/values.yaml
@@ -0,0 +1,1059 @@
+rbac:
+ create: true
+
+## Define serviceAccount names for components. Defaults to component's fully qualified name.
+##
+serviceAccounts:
+ alertmanager:
+ create: true
+ name:
+ kubeStateMetrics:
+ create: true
+ name:
+ nodeExporter:
+ create: true
+ name:
+ pushgateway:
+ create: true
+ name:
+ server:
+ create: true
+ name:
+
+alertmanager:
+ ## If false, alertmanager will not be installed
+ ##
+ enabled: true
+
+ ## alertmanager container name
+ ##
+ name: alertmanager
+
+ ## alertmanager container image
+ ##
+ image:
+ repository: prom/alertmanager
+ tag: v0.15.2
+ pullPolicy: IfNotPresent
+
+ ## alertmanager priorityClassName
+ ##
+ priorityClassName: ""
+
+ ## Additional alertmanager container arguments
+ ##
+ extraArgs: {}
+
+ ## The URL prefix at which the container can be accessed. Useful in the case the '-web.external-url' includes a slug
+ ## so that the various internal URLs are still able to access as they are in the default case.
+ ## (Optional)
+ prefixURL: ""
+
+ ## External URL which can access alertmanager
+ ## Maybe same with Ingress host name
+ baseURL: "/"
+
+ ## Additional alertmanager container environment variable
+ ## For instance to add a http_proxy
+ ##
+ extraEnv: {}
+
+ ## ConfigMap override where fullname is {{.Release.Name}}-{{.Values.alertmanager.configMapOverrideName}}
+ ## Defining configMapOverrideName will cause templates/alertmanager-configmap.yaml
+ ## to NOT generate a ConfigMap resource
+ ##
+ configMapOverrideName: ""
+
+ ingress:
+ ## If true, alertmanager Ingress will be created
+ ##
+ enabled: false
+
+ ## alertmanager Ingress annotations
+ ##
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: 'true'
+
+ ## alertmanager Ingress additional labels
+ ##
+ extraLabels: {}
+
+ ## alertmanager Ingress hostnames with optional path
+ ## Must be provided if Ingress is enabled
+ ##
+ hosts: []
+ # - alertmanager.domain.com
+ # - domain.com/alertmanager
+
+ ## alertmanager Ingress TLS configuration
+ ## Secrets must be manually created in the namespace
+ ##
+ tls: []
+ # - secretName: prometheus-alerts-tls
+ # hosts:
+ # - alertmanager.domain.com
+
+ ## Alertmanager Deployment Strategy type
+ # strategy:
+ # type: Recreate
+
+ ## Node tolerations for alertmanager scheduling to nodes with taints
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
+ ##
+ tolerations: []
+ # - key: "key"
+ # operator: "Equal|Exists"
+ # value: "value"
+ # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)"
+
+ ## Node labels for alertmanager pod assignment
+ ## Ref: https://kubernetes.io/docs/user-guide/node-selection/
+ ##
+ nodeSelector: {}
+
+ ## Pod affinity
+ ##
+ affinity: {}
+
+ ## Use an alternate scheduler, e.g. "stork".
+ ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/
+ ##
+ # schedulerName:
+
+ persistentVolume:
+ ## If true, alertmanager will create/use a Persistent Volume Claim
+ ## If false, use emptyDir
+ ##
+ enabled: true
+
+ ## alertmanager data Persistent Volume access modes
+ ## Must match those of existing PV or dynamic provisioner
+ ## Ref: http://kubernetes.io/docs/user-guide/persistent-volumes/
+ ##
+ accessModes:
+ - ReadWriteOnce
+
+ ## alertmanager data Persistent Volume Claim annotations
+ ##
+ annotations: {}
+
+ ## alertmanager data Persistent Volume existing claim name
+ ## Requires alertmanager.persistentVolume.enabled: true
+ ## If defined, PVC must be created manually before volume will be bound
+ existingClaim: ""
+
+ ## alertmanager data Persistent Volume mount root path
+ ##
+ mountPath: /data
+
+ ## alertmanager data Persistent Volume size
+ ##
+ size: 2Gi
+
+ ## alertmanager data Persistent Volume Storage Class
+ ## If defined, storageClassName:
+ ## If set to "-", storageClassName: "", which disables dynamic provisioning
+ ## If undefined (the default) or set to null, no storageClassName spec is
+ ## set, choosing the default provisioner. (gp2 on AWS, standard on
+ ## GKE, AWS & OpenStack)
+ ##
+ # storageClass: "-"
+
+ ## Subdirectory of alertmanager data Persistent Volume to mount
+ ## Useful if the volume's root directory is not empty
+ ##
+ subPath: ""
+
+ ## Annotations to be added to alertmanager pods
+ ##
+ podAnnotations: {}
+
+ replicaCount: 1
+
+ ## alertmanager resource requests and limits
+ ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ # limits:
+ # cpu: 10m
+ # memory: 32Mi
+ # requests:
+ # cpu: 10m
+ # memory: 32Mi
+
+ ## Security context to be added to alertmanager pods
+ ##
+ securityContext: {}
+
+ service:
+ annotations: {}
+ labels: {}
+ clusterIP: ""
+
+ ## Enabling peer mesh service end points for enabling the HA alert manager
+ ## Ref: https://github.com/prometheus/alertmanager/blob/master/README.md
+ # enableMeshPeer : true
+
+ ## List of IP addresses at which the alertmanager service is available
+ ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips
+ ##
+ externalIPs: []
+
+ loadBalancerIP: ""
+ loadBalancerSourceRanges: []
+ servicePort: 80
+ # nodePort: 30000
+ type: ClusterIP
+
+## Monitors ConfigMap changes and POSTs to a URL
+## Ref: https://github.com/jimmidyson/configmap-reload
+##
+configmapReload:
+ ## configmap-reload container name
+ ##
+ name: configmap-reload
+
+ ## configmap-reload container image
+ ##
+ image:
+ repository: jimmidyson/configmap-reload
+ tag: v0.2.2
+ pullPolicy: IfNotPresent
+
+ ## Additional configmap-reload container arguments
+ ##
+ extraArgs: {}
+
+ ## Additional configmap-reload mounts
+ ##
+ extraConfigmapMounts: []
+ # - name: prometheus-alerts
+ # mountPath: /etc/alerts.d
+ # configMap: prometheus-alerts
+ # readOnly: true
+
+
+ ## configmap-reload resource requests and limits
+ ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+
+initChownData:
+ ## If false, data ownership will not be reset at startup
+ ## This allows the prometheus-server to be run with an arbitrary user
+ ##
+ enabled: true
+
+ ## initChownData container name
+ ##
+ name: init-chown-data
+
+ ## initChownData container image
+ ##
+ image:
+ repository: busybox
+ tag: latest
+ pullPolicy: IfNotPresent
+
+ ## initChownData resource requests and limits
+ ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+
+kubeStateMetrics:
+ ## If false, kube-state-metrics will not be installed
+ ##
+ enabled: true
+
+ ## kube-state-metrics container name
+ ##
+ name: kube-state-metrics
+
+ ## kube-state-metrics container image
+ ##
+ image:
+ repository: quay.io/coreos/kube-state-metrics
+ tag: v1.4.0
+ pullPolicy: IfNotPresent
+
+ ## kube-state-metrics priorityClassName
+ ##
+ priorityClassName: ""
+
+ ## kube-state-metrics container arguments
+ ##
+ args: {}
+
+ ## Node tolerations for kube-state-metrics scheduling to nodes with taints
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
+ ##
+ tolerations: []
+ # - key: "key"
+ # operator: "Equal|Exists"
+ # value: "value"
+ # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)"
+
+ ## Node labels for kube-state-metrics pod assignment
+ ## Ref: https://kubernetes.io/docs/user-guide/node-selection/
+ ##
+ nodeSelector: {}
+
+ ## Annotations to be added to kube-state-metrics pods
+ ##
+ podAnnotations: {}
+
+ pod:
+ labels: {}
+
+ replicaCount: 1
+
+ ## kube-state-metrics resource requests and limits
+ ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ # limits:
+ # cpu: 10m
+ # memory: 16Mi
+ # requests:
+ # cpu: 10m
+ # memory: 16Mi
+
+ ## Security context to be added to kube-state-metrics pods
+ ##
+ securityContext: {}
+
+ service:
+ annotations:
+ prometheus.io/scrape: "true"
+ labels: {}
+
+ # Exposed as a headless service:
+ # https://kubernetes.io/docs/concepts/services-networking/service/#headless-services
+ clusterIP: None
+
+ ## List of IP addresses at which the kube-state-metrics service is available
+ ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips
+ ##
+ externalIPs: []
+
+ loadBalancerIP: ""
+ loadBalancerSourceRanges: []
+ servicePort: 80
+ type: ClusterIP
+
+nodeExporter:
+ ## If false, node-exporter will not be installed
+ ##
+ enabled: true
+
+ ## If true, node-exporter pods share the host network namespace
+ ##
+ hostNetwork: true
+
+ ## If true, node-exporter pods share the host PID namespace
+ ##
+ hostPID: true
+
+ ## node-exporter container name
+ ##
+ name: node-exporter
+
+ ## node-exporter container image
+ ##
+ image:
+ repository: prom/node-exporter
+ tag: v0.15.2
+ pullPolicy: IfNotPresent
+
+ ## node-exporter priorityClassName
+ ##
+ priorityClassName: ""
+
+ ## Custom Update Strategy
+ ##
+ updateStrategy:
+ type: OnDelete
+
+ ## Additional node-exporter container arguments
+ ##
+ extraArgs: {}
+
+ ## Additional node-exporter hostPath mounts
+ ##
+ extraHostPathMounts: []
+ # - name: textfile-dir
+ # mountPath: /srv/txt_collector
+ # hostPath: /var/lib/node-exporter
+ # readOnly: true
+
+ extraConfigmapMounts: []
+ # - name: certs-configmap
+ # mountPath: /prometheus
+ # configMap: certs-configmap
+ # readOnly: true
+
+ ## Node tolerations for node-exporter scheduling to nodes with taints
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
+ ##
+ tolerations: []
+ # - key: "key"
+ # operator: "Equal|Exists"
+ # value: "value"
+ # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)"
+
+ ## Node labels for node-exporter pod assignment
+ ## Ref: https://kubernetes.io/docs/user-guide/node-selection/
+ ##
+ nodeSelector: {}
+
+ ## Annotations to be added to node-exporter pods
+ ##
+ podAnnotations: {}
+
+ ## Labels to be added to node-exporter pods
+ ##
+ pod:
+ labels: {}
+
+ ## node-exporter resource limits & requests
+ ## Ref: https://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ # limits:
+ # cpu: 200m
+ # memory: 50Mi
+ # requests:
+ # cpu: 100m
+ # memory: 30Mi
+
+ ## Security context to be added to node-exporter pods
+ ##
+ securityContext: {}
+ # runAsUser: 0
+
+ service:
+ annotations:
+ prometheus.io/scrape: "true"
+ labels: {}
+
+ # Exposed as a headless service:
+ # https://kubernetes.io/docs/concepts/services-networking/service/#headless-services
+ clusterIP: None
+
+ ## List of IP addresses at which the node-exporter service is available
+ ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips
+ ##
+ externalIPs: []
+
+ hostPort: 9100
+ loadBalancerIP: ""
+ loadBalancerSourceRanges: []
+ servicePort: 9100
+ type: ClusterIP
+
+server:
+ ## Prometheus server container name
+ ##
+ name: server
+
+ ## Prometheus server container image
+ ##
+ image:
+ repository: prom/prometheus
+ tag: v2.4.3
+ pullPolicy: IfNotPresent
+
+ ## prometheus server priorityClassName
+ ##
+ priorityClassName: ""
+
+ ## The URL prefix at which the container can be accessed. Useful in the case the '-web.external-url' includes a slug
+ ## so that the various internal URLs are still able to access as they are in the default case.
+ ## (Optional)
+ prefixURL: ""
+
+ ## External URL which can access alertmanager
+ ## Maybe same with Ingress host name
+ baseURL: ""
+
+ ## This flag controls access to the administrative HTTP API which includes functionality such as deleting time
+ ## series. This is disabled by default.
+ enableAdminApi: false
+
+ global:
+ ## How frequently to scrape targets by default
+ ##
+ scrape_interval: 1m
+ ## How long until a scrape request times out
+ ##
+ scrape_timeout: 10s
+ ## How frequently to evaluate rules
+ ##
+ evaluation_interval: 1m
+
+ ## Additional Prometheus server container arguments
+ ##
+ extraArgs: {}
+
+ ## Additional Prometheus server hostPath mounts
+ ##
+ extraHostPathMounts: []
+ # - name: certs-dir
+ # mountPath: /etc/kubernetes/certs
+ # hostPath: /etc/kubernetes/certs
+ # readOnly: true
+
+ extraConfigmapMounts: []
+ # - name: certs-configmap
+ # mountPath: /prometheus
+ # configMap: certs-configmap
+ # readOnly: true
+
+ ## Additional Prometheus server Secret mounts
+ # Defines additional mounts with secrets. Secrets must be manually created in the namespace.
+ extraSecretMounts: []
+ # - name: secret-files
+ # mountPath: /etc/secrets
+ # secretName: prom-secret-files
+ # readOnly: true
+
+ ## ConfigMap override where fullname is {{.Release.Name}}-{{.Values.server.configMapOverrideName}}
+ ## Defining configMapOverrideName will cause templates/server-configmap.yaml
+ ## to NOT generate a ConfigMap resource
+ ##
+ configMapOverrideName: ""
+
+ ingress:
+ ## If true, Prometheus server Ingress will be created
+ ##
+ enabled: false
+
+ ## Prometheus server Ingress annotations
+ ##
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: 'true'
+
+ ## Prometheus server Ingress additional labels
+ ##
+ extraLabels: {}
+
+ ## Prometheus server Ingress hostnames with optional path
+ ## Must be provided if Ingress is enabled
+ ##
+ hosts: []
+ # - prometheus.domain.com
+ # - domain.com/prometheus
+
+ ## Prometheus server Ingress TLS configuration
+ ## Secrets must be manually created in the namespace
+ ##
+ tls: []
+ # - secretName: prometheus-server-tls
+ # hosts:
+ # - prometheus.domain.com
+
+ ## Server Deployment Strategy type
+ # strategy:
+ # type: Recreate
+
+ ## Node tolerations for server scheduling to nodes with taints
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
+ ##
+ tolerations: []
+ # - key: "key"
+ # operator: "Equal|Exists"
+ # value: "value"
+ # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)"
+
+ ## Node labels for Prometheus server pod assignment
+ ## Ref: https://kubernetes.io/docs/user-guide/node-selection/
+ ##
+ nodeSelector: {}
+
+ ## Pod affinity
+ ##
+ affinity: {}
+
+ ## Use an alternate scheduler, e.g. "stork".
+ ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/
+ ##
+ # schedulerName:
+
+ persistentVolume:
+ ## If true, Prometheus server will create/use a Persistent Volume Claim
+ ## If false, use emptyDir
+ ##
+ enabled: true
+
+ ## Prometheus server data Persistent Volume access modes
+ ## Must match those of existing PV or dynamic provisioner
+ ## Ref: http://kubernetes.io/docs/user-guide/persistent-volumes/
+ ##
+ accessModes:
+ - ReadWriteOnce
+
+ ## Prometheus server data Persistent Volume annotations
+ ##
+ annotations: {}
+
+ ## Prometheus server data Persistent Volume existing claim name
+ ## Requires server.persistentVolume.enabled: true
+ ## If defined, PVC must be created manually before volume will be bound
+ existingClaim: ""
+
+ ## Prometheus server data Persistent Volume mount root path
+ ##
+ mountPath: /data
+
+ ## Prometheus server data Persistent Volume size
+ ##
+ size: 8Gi
+
+ ## Prometheus server data Persistent Volume Storage Class
+ ## If defined, storageClassName:
+ ## If set to "-", storageClassName: "", which disables dynamic provisioning
+ ## If undefined (the default) or set to null, no storageClassName spec is
+ ## set, choosing the default provisioner. (gp2 on AWS, standard on
+ ## GKE, AWS & OpenStack)
+ ##
+ # storageClass: "-"
+
+ ## Subdirectory of Prometheus server data Persistent Volume to mount
+ ## Useful if the volume's root directory is not empty
+ ##
+ subPath: ""
+
+ ## Annotations to be added to Prometheus server pods
+ ##
+ podAnnotations: {}
+ # iam.amazonaws.com/role: prometheus
+
+ replicaCount: 1
+
+ ## Prometheus server resource requests and limits
+ ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ # limits:
+ # cpu: 500m
+ # memory: 512Mi
+ # requests:
+ # cpu: 500m
+ # memory: 512Mi
+
+ ## Security context to be added to server pods
+ ##
+ securityContext: {}
+
+ service:
+ annotations: {}
+ labels: {}
+ clusterIP: ""
+
+ ## List of IP addresses at which the Prometheus server service is available
+ ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips
+ ##
+ externalIPs: []
+
+ loadBalancerIP: ""
+ loadBalancerSourceRanges: []
+ servicePort: 80
+ type: ClusterIP
+
+ ## Prometheus server pod termination grace period
+ ##
+ terminationGracePeriodSeconds: 300
+
+ ## Prometheus data retention period (i.e 360h)
+ ##
+ retention: ""
+
+pushgateway:
+ ## If false, pushgateway will not be installed
+ ##
+ enabled: true
+
+ ## pushgateway container name
+ ##
+ name: pushgateway
+
+ ## pushgateway container image
+ ##
+ image:
+ repository: prom/pushgateway
+ tag: v0.5.2
+ pullPolicy: IfNotPresent
+
+ ## pushgateway priorityClassName
+ ##
+ priorityClassName: ""
+
+ ## Additional pushgateway container arguments
+ ##
+ extraArgs: {}
+
+ ingress:
+ ## If true, pushgateway Ingress will be created
+ ##
+ enabled: false
+
+ ## pushgateway Ingress annotations
+ ##
+ annotations: {}
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: 'true'
+
+ ## pushgateway Ingress hostnames with optional path
+ ## Must be provided if Ingress is enabled
+ ##
+ hosts: []
+ # - pushgateway.domain.com
+ # - domain.com/pushgateway
+
+ ## pushgateway Ingress TLS configuration
+ ## Secrets must be manually created in the namespace
+ ##
+ tls: []
+ # - secretName: prometheus-alerts-tls
+ # hosts:
+ # - pushgateway.domain.com
+
+ ## Node tolerations for pushgateway scheduling to nodes with taints
+ ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
+ ##
+ tolerations: []
+ # - key: "key"
+ # operator: "Equal|Exists"
+ # value: "value"
+ # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)"
+
+ ## Node labels for pushgateway pod assignment
+ ## Ref: https://kubernetes.io/docs/user-guide/node-selection/
+ ##
+ nodeSelector: {}
+
+ ## Annotations to be added to pushgateway pods
+ ##
+ podAnnotations: {}
+
+ replicaCount: 1
+
+ ## pushgateway resource requests and limits
+ ## Ref: http://kubernetes.io/docs/user-guide/compute-resources/
+ ##
+ resources: {}
+ # limits:
+ # cpu: 10m
+ # memory: 32Mi
+ # requests:
+ # cpu: 10m
+ # memory: 32Mi
+
+ ## Security context to be added to push-gateway pods
+ ##
+ securityContext: {}
+
+ service:
+ annotations:
+ prometheus.io/probe: pushgateway
+ labels: {}
+ clusterIP: ""
+
+ ## List of IP addresses at which the pushgateway service is available
+ ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips
+ ##
+ externalIPs: []
+
+ loadBalancerIP: ""
+ loadBalancerSourceRanges: []
+ servicePort: 9091
+ type: ClusterIP
+
+## alertmanager ConfigMap entries
+##
+alertmanagerFiles:
+ alertmanager.yml:
+ global: {}
+ # slack_api_url: ''
+
+ receivers:
+ - name: default-receiver
+ # slack_configs:
+ # - channel: '@you'
+ # send_resolved: true
+
+ route:
+ group_wait: 10s
+ group_interval: 5m
+ receiver: default-receiver
+ repeat_interval: 3h
+
+## Prometheus server ConfigMap entries
+##
+serverFiles:
+ alerts: {}
+ rules: {}
+
+ prometheus.yml:
+ rule_files:
+ - /etc/config/rules
+ - /etc/config/alerts
+
+ scrape_configs:
+ - job_name: prometheus
+ static_configs:
+ - targets:
+ - localhost:9090
+
+ # A scrape configuration for running Prometheus on a Kubernetes cluster.
+ # This uses separate scrape configs for cluster components (i.e. API server, node)
+ # and services to allow each to use different authentication configs.
+ #
+ # Kubernetes labels will be added as Prometheus labels on metrics via the
+ # `labelmap` relabeling action.
+
+ # Scrape config for API servers.
+ #
+ # Kubernetes exposes API servers as endpoints to the default/kubernetes
+ # service so this uses `endpoints` role and uses relabelling to only keep
+ # the endpoints associated with the default/kubernetes service using the
+ # default named port `https`. This works for single API server deployments as
+ # well as HA API server deployments.
+ - job_name: 'kubernetes-apiservers'
+
+ kubernetes_sd_configs:
+ - role: endpoints
+
+ # Default to scraping over https. If required, just disable this or change to
+ # `http`.
+ scheme: https
+
+ # This TLS & bearer token file config is used to connect to the actual scrape
+ # endpoints for cluster components. This is separate to discovery auth
+ # configuration because discovery & scraping are two separate concerns in
+ # Prometheus. The discovery auth config is automatic if Prometheus runs inside
+ # the cluster. Otherwise, more config options have to be provided within the
+ # .
+ tls_config:
+ ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
+ # If your node certificates are self-signed or use a different CA to the
+ # master CA, then disable certificate verification below. Note that
+ # certificate verification is an integral part of a secure infrastructure
+ # so this should only be disabled in a controlled environment. You can
+ # disable certificate verification by uncommenting the line below.
+ #
+ insecure_skip_verify: true
+ bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
+
+ # Keep only the default/kubernetes service endpoints for the https port. This
+ # will add targets for each API server which Kubernetes adds an endpoint to
+ # the default/kubernetes service.
+ relabel_configs:
+ - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
+ action: keep
+ regex: default;kubernetes;https
+
+ - job_name: 'kubernetes-nodes'
+
+ # Default to scraping over https. If required, just disable this or change to
+ # `http`.
+ scheme: https
+
+ # This TLS & bearer token file config is used to connect to the actual scrape
+ # endpoints for cluster components. This is separate to discovery auth
+ # configuration because discovery & scraping are two separate concerns in
+ # Prometheus. The discovery auth config is automatic if Prometheus runs inside
+ # the cluster. Otherwise, more config options have to be provided within the
+ # .
+ tls_config:
+ ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
+ # If your node certificates are self-signed or use a different CA to the
+ # master CA, then disable certificate verification below. Note that
+ # certificate verification is an integral part of a secure infrastructure
+ # so this should only be disabled in a controlled environment. You can
+ # disable certificate verification by uncommenting the line below.
+ #
+ insecure_skip_verify: true
+ bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
+
+ kubernetes_sd_configs:
+ - role: node
+
+ relabel_configs:
+ - action: labelmap
+ regex: __meta_kubernetes_node_label_(.+)
+ - target_label: __address__
+ replacement: kubernetes.default.svc:443
+ - source_labels: [__meta_kubernetes_node_name]
+ regex: (.+)
+ target_label: __metrics_path__
+ replacement: /api/v1/nodes/${1}/proxy/metrics
+
+
+ - job_name: 'kubernetes-nodes-cadvisor'
+
+ # Default to scraping over https. If required, just disable this or change to
+ # `http`.
+ scheme: https
+
+ # This TLS & bearer token file config is used to connect to the actual scrape
+ # endpoints for cluster components. This is separate to discovery auth
+ # configuration because discovery & scraping are two separate concerns in
+ # Prometheus. The discovery auth config is automatic if Prometheus runs inside
+ # the cluster. Otherwise, more config options have to be provided within the
+ # .
+ tls_config:
+ ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
+ # If your node certificates are self-signed or use a different CA to the
+ # master CA, then disable certificate verification below. Note that
+ # certificate verification is an integral part of a secure infrastructure
+ # so this should only be disabled in a controlled environment. You can
+ # disable certificate verification by uncommenting the line below.
+ #
+ insecure_skip_verify: true
+ bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
+
+ kubernetes_sd_configs:
+ - role: node
+
+ # This configuration will work only on kubelet 1.7.3+
+ # As the scrape endpoints for cAdvisor have changed
+ # if you are using older version you need to change the replacement to
+ # replacement: /api/v1/nodes/${1}:4194/proxy/metrics
+ # more info here https://github.com/coreos/prometheus-operator/issues/633
+ relabel_configs:
+ - action: labelmap
+ regex: __meta_kubernetes_node_label_(.+)
+ - target_label: __address__
+ replacement: kubernetes.default.svc:443
+ - source_labels: [__meta_kubernetes_node_name]
+ regex: (.+)
+ target_label: __metrics_path__
+ replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
+
+ # Scrape config for service endpoints.
+ #
+ # The relabeling allows the actual service scrape endpoint to be configured
+ # via the following annotations:
+ #
+ # * `prometheus.io/scrape`: Only scrape services that have a value of `true`
+ # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need
+ # to set this to `https` & most likely set the `tls_config` of the scrape config.
+ # * `prometheus.io/path`: If the metrics path is not `/metrics` override this.
+ # * `prometheus.io/port`: If the metrics are exposed on a different port to the
+ # service then set this appropriately.
+ - job_name: 'kubernetes-service-endpoints'
+
+ kubernetes_sd_configs:
+ - role: endpoints
+
+ relabel_configs:
+ - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
+ action: keep
+ regex: true
+ - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
+ action: replace
+ target_label: __scheme__
+ regex: (https?)
+ - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
+ action: replace
+ target_label: __metrics_path__
+ regex: (.+)
+ - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
+ action: replace
+ target_label: __address__
+ regex: ([^:]+)(?::\d+)?;(\d+)
+ replacement: $1:$2
+ - action: labelmap
+ regex: __meta_kubernetes_service_label_(.+)
+ - source_labels: [__meta_kubernetes_namespace]
+ action: replace
+ target_label: kubernetes_namespace
+ - source_labels: [__meta_kubernetes_service_name]
+ action: replace
+ target_label: kubernetes_name
+
+ - job_name: 'prometheus-pushgateway'
+ honor_labels: true
+
+ kubernetes_sd_configs:
+ - role: service
+
+ relabel_configs:
+ - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
+ action: keep
+ regex: pushgateway
+
+ # Example scrape config for probing services via the Blackbox Exporter.
+ #
+ # The relabeling allows the actual service scrape endpoint to be configured
+ # via the following annotations:
+ #
+ # * `prometheus.io/probe`: Only probe services that have a value of `true`
+ - job_name: 'kubernetes-services'
+
+ metrics_path: /probe
+ params:
+ module: [http_2xx]
+
+ kubernetes_sd_configs:
+ - role: service
+
+ relabel_configs:
+ - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
+ action: keep
+ regex: true
+ - source_labels: [__address__]
+ target_label: __param_target
+ - target_label: __address__
+ replacement: blackbox
+ - source_labels: [__param_target]
+ target_label: instance
+ - action: labelmap
+ regex: __meta_kubernetes_service_label_(.+)
+ - source_labels: [__meta_kubernetes_namespace]
+ target_label: kubernetes_namespace
+ - source_labels: [__meta_kubernetes_service_name]
+ target_label: kubernetes_name
+
+ # Example scrape config for pods
+ #
+ # The relabeling allows the actual pod scrape endpoint to be configured via the
+ # following annotations:
+ #
+ # * `prometheus.io/scrape`: Only scrape pods that have a value of `true`
+ # * `prometheus.io/path`: If the metrics path is not `/metrics` override this.
+ # * `prometheus.io/port`: Scrape the pod on the indicated port instead of the default of `9102`.
+ - job_name: 'kubernetes-pods'
+
+ kubernetes_sd_configs:
+ - role: pod
+
+ relabel_configs:
+ - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
+ action: keep
+ regex: true
+ - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
+ action: replace
+ target_label: __metrics_path__
+ regex: (.+)
+ - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
+ action: replace
+ regex: ([^:]+)(?::\d+)?;(\d+)
+ replacement: $1:$2
+ target_label: __address__
+ - action: labelmap
+ regex: __meta_kubernetes_pod_label_(.+)
+ - source_labels: [__meta_kubernetes_namespace]
+ action: replace
+ target_label: kubernetes_namespace
+ - source_labels: [__meta_kubernetes_pod_name]
+ action: replace
+ target_label: kubernetes_pod_name
+
+networkPolicy:
+ ## Enable creation of NetworkPolicy resources.
+ ##
+ enabled: false
diff --git a/manifests/redis-cluster/my-values.yaml b/manifests/redis-cluster/my-values.yaml
new file mode 100644
index 0000000..1dc1dc6
--- /dev/null
+++ b/manifests/redis-cluster/my-values.yaml
@@ -0,0 +1,120 @@
+## Configure resource requests and limits
+## ref: http://kubernetes.io/docs/user-guide/compute-resources/
+##
+image:
+ repository: redis
+ tag: 5.0.3-alpine
+ pullPolicy: IfNotPresent
+## replicas number for each component
+replicas: 2
+
+## Redis specific configuration options
+redis:
+ port: 6379
+ masterGroupName: mymaster
+ config:
+ ## Additional redis conf options can be added below
+ ## For all available options see http://download.redis.io/redis-stable/redis.conf
+ min-slaves-to-write: 1
+ min-slaves-max-lag: 5 # Value in seconds
+ maxmemory: "1g" # Max memory to use for each redis instance. Default is unlimited.
+ maxmemory-policy: "allkeys-lru" # Max memory policy to use for each redis instance. Default is volatile-lru.
+ # Determines if scheduled RDB backups are created. Default is false.
+ # Please note that local (on-disk) RDBs will still be created when re-syncing with a new slave. The only way to prevent this is to enable diskless replication.
+ # save: "900 1"
+ # When enabled, directly sends the RDB over the wire to slaves, without using the disk as intermediate storage. Default is false.
+ repl-diskless-sync: "yes"
+ rdbcompression: "yes"
+ rdbchecksum: "yes"
+
+ ## Custom redis.conf files used to override default settings. If this file is
+ ## specified then the redis.config above will be ignored.
+ # customConfig: |-
+ # Define configuration here
+
+ resources:
+ requests:
+ memory: 500Mi
+ cpu: 100m
+ limits:
+ memory: 1100Mi
+
+## Sentinel specific configuration options
+sentinel:
+ port: 26379
+ quorum: 1
+ config:
+ ## Additional sentinel conf options can be added below. Only options that
+ ## are expressed in the format simialar to 'sentinel xxx mymaster xxx' will
+ ## be properly templated.
+ ## For available options see http://download.redis.io/redis-stable/sentinel.conf
+ down-after-milliseconds: 10000
+ ## Failover timeout value in milliseconds
+ failover-timeout: 180000
+ parallel-syncs: 5
+
+ ## Custom sentinel.conf files used to override default settings. If this file is
+ ## specified then the sentinel.config above will be ignored.
+ # customConfig: |-
+ # Define configuration here
+
+ resources:
+ requests:
+ memory: 200Mi
+ cpu: 100m
+ limits:
+ memory: 200Mi
+
+securityContext:
+ runAsUser: 1000
+ fsGroup: 1000
+ runAsNonRoot: true
+
+## Node labels, affinity, and tolerations for pod assignment
+## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
+## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature
+## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+affinity: |
+ podAntiAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - labelSelector:
+ matchLabels:
+ app: {{ template "redis-ha.name" . }}
+ release: {{ .Release.Name }}
+ topologyKey: kubernetes.io/hostname
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - weight: 100
+ podAffinityTerm:
+ labelSelector:
+ matchLabels:
+ app: {{ template "redis-ha.name" . }}
+ release: {{ .Release.Name }}
+ topologyKey: failure-domain.beta.kubernetes.io/zone
+
+podDisruptionBudget: {}
+ # maxUnavailable: 1
+ # minAvailable: 1
+
+## Configures redis with AUTH (requirepass & masterauth conf params)
+auth: true
+redisPassword: redis1234
+
+## Use existing secret containing "auth" key (ignores redisPassword)
+# existingSecret:
+
+persistentVolume:
+ enabled: false
+ ## redis-ha data Persistent Volume Storage Class
+ ## If defined, storageClassName:
+ ## If set to "-", storageClassName: "", which disables dynamic provisioning
+ ## If undefined (the default) or set to null, no storageClassName spec is
+ ## set, choosing the default provisioner. (gp2 on AWS, standard on
+ ## GKE, AWS & OpenStack)
+ ##
+ storageClass: "nfs-db"
+ accessModes:
+ - ReadWriteOnce
+ size: 3Gi
+ annotations: {}
+init:
+ resources: {}
diff --git a/manifests/redis-cluster/redis-ha/Chart.yaml b/manifests/redis-cluster/redis-ha/Chart.yaml
new file mode 100644
index 0000000..be92ddb
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/Chart.yaml
@@ -0,0 +1,20 @@
+name: redis-ha
+home: http://redis.io/
+engine: gotpl
+keywords:
+- redis
+- keyvalue
+- database
+version: 3.1.3
+appVersion: 5.0.3
+description: Highly available Kubernetes implementation of Redis
+icon: https://upload.wikimedia.org/wikipedia/en/thumb/6/6b/Redis_Logo.svg/1200px-Redis_Logo.svg.png
+maintainers:
+- email: salimsalaues@gmail.com
+ name: ssalaues
+details:
+ This Helm chart provides a highly available Redis implementation with a master/slave configuration
+ and uses Sentinel sidecars for failover management
+sources:
+- https://redis.io/download
+- https://github.com/scality/Zenko/tree/development/1.0/kubernetes/zenko/charts/redis-ha
diff --git a/manifests/redis-cluster/redis-ha/OWNERS b/manifests/redis-cluster/redis-ha/OWNERS
new file mode 100644
index 0000000..c1ffbbc
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/OWNERS
@@ -0,0 +1,4 @@
+approvers:
+- ssalaues
+reviewers:
+- ssalaues
diff --git a/manifests/redis-cluster/redis-ha/README.md b/manifests/redis-cluster/redis-ha/README.md
new file mode 100644
index 0000000..9a16ff6
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/README.md
@@ -0,0 +1,117 @@
+# Redis
+
+[Redis](http://redis.io/) is an advanced key-value cache and store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets, sorted sets, bitmaps and hyperloglogs.
+
+## TL;DR;
+
+```bash
+$ helm install stable/redis-ha
+```
+
+By default this chart install 3 pods total:
+ * one pod containing a redis master and sentinel containers
+ * two pods each containing redis slave and sentinel containers.
+
+## Introduction
+
+This chart bootstraps a [Redis](https://redis.io) highly available master/slave statefulset in a [Kubernetes](http://kubernetes.io) cluster using the Helm package manager.
+
+## Prerequisites
+
+- Kubernetes 1.8+ with Beta APIs enabled
+- PV provisioner support in the underlying infrastructure
+
+## Upgrading the Chart
+
+Please note that there have been a number of changes simplifying the redis management strategy (for better failover and elections) in the 3.x version of this chart. These changes allow the use of official [redis](https://hub.docker.com/_/redis/) images that do not require special RBAC or ServiceAccount roles. As a result when upgrading from version >=2.0.1 to >=3.0.0 of this chart, `Role`, `RoleBinding`, and `ServiceAccount` resources should be deleted manually.
+
+## Installing the Chart
+
+To install the chart
+
+```bash
+$ helm install stable/redis-ha
+```
+
+The command deploys Redis on the Kubernetes cluster in the default configuration. By default this chart install one master pod containing redis master container and sentinel container along with 2 redis slave pods each containing their own sentinel sidecars. The [configuration](#configuration) section lists the parameters that can be configured during installation.
+
+> **Tip**: List all releases using `helm list`
+
+## Uninstalling the Chart
+
+To uninstall/delete the deployment:
+
+```bash
+$ helm delete
+```
+
+The command removes all the Kubernetes components associated with the chart and deletes the release.
+
+## Configuration
+
+The following table lists the configurable parameters of the Redis chart and their default values.
+
+| Parameter | Description | Default |
+| -------------------------------- | ----------------------------------------------------- | --------------------------------------------------------- |
+| `image` | Redis image | `redis` |
+| `tag` | Redis tag | `5.0.3-alpine` |
+| `replicas` | Number of redis master/slave pods | `3` |
+| `redis.port` | Port to access the redis service | `6379` |
+| `redis.masterGroupName` | Redis convention for naming the cluster group | `mymaster` |
+| `redis.config` | Any valid redis config options in this section will be applied to each server (see below) | see values.yaml |
+| `redis.customConfig` | Allows for custom redis.conf files to be applied. If this is used then `redis.config` is ignored | `` |
+| `redis.resources` | CPU/Memory for master/slave nodes resource requests/limits | `{}` |
+| `sentinel.port` | Port to access the sentinel service | `26379` |
+| `sentinel.quorum` | Minimum number of servers necessary to maintain quorum | `2` |
+| `sentinel.config` | Valid sentinel config options in this section will be applied as config options to each sentinel (see below) | see values.yaml |
+| `sentinel.customConfig` | Allows for custom sentinel.conf files to be applied. If this is used then `sentinel.config` is ignored | `` |
+| `sentinel.resources` | CPU/Memory for sentinel node resource requests/limits | `{}` |
+| `init.resources` | CPU/Memory for init Container node resource requests/limits | `{}`
+| `auth` | Enables or disables redis AUTH (Requires `redisPassword` to be set) | `false` |
+| `redisPassword` | A password that configures a `requirepass` and `masterauth` in the conf parameters (Requires `auth: enabled`) | `` |
+| `existingSecret` | An existing secret containing an `auth` key that configures `requirepass` and `masterauth` in the conf parameters (Requires `auth: enabled`, cannot be used in conjunction with `.Values.redisPassword`) | `` |
+| `nodeSelector` | Node labels for pod assignment | `{}` |
+| `tolerations` | Toleration labels for pod assignment | `[]` |
+| `podAntiAffinity.server` | Antiaffinity for pod assignment of servers, `hard` or `soft` | `Hard node and soft zone anti-affinity` |
+
+
+Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
+
+```bash
+$ helm install \
+ --set image=redis \
+ --set tag=5.0.3-alpine \
+ stable/redis-ha
+```
+
+The above command sets the Redis server within `default` namespace.
+
+Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example,
+
+```bash
+$ helm install -f values.yaml stable/redis-ha
+```
+
+> **Tip**: You can use the default [values.yaml](values.yaml)
+
+## Custom Redis and Sentinel config options
+
+This chart allows for most redis or sentinel config options to be passed as a key value pair through the `values.yaml` under `redis.config` and `sentinel.config`. See links below for all available options.
+
+[Example redis.conf](http://download.redis.io/redis-stable/redis.conf)
+[Example sentinel.conf](http://download.redis.io/redis-stable/sentinel.conf)
+
+For example `repl-timeout 60` would be added to the `redis.config` section of the `values.yaml` as:
+
+```yml
+ repl-timeout: "60"
+```
+
+Sentinel options supported must be in the the `sentinel ` format. For example, `sentinel down-after-milliseconds 30000` would be added to the `sentinel.config` section of the `values.yaml` as:
+
+```yml
+ down-after-milliseconds: 30000
+```
+
+If more control is needed from either the redis or sentinel config then an entire config can be defined under `redis.customConfig` or `sentinel.customConfig`. Please note that these values will override any configuration options under their respective section. For example, if you define `sentinel.customConfig` then the `sentinel.config` is ignored.
+
diff --git a/manifests/redis-cluster/redis-ha/templates/NOTES.txt b/manifests/redis-cluster/redis-ha/templates/NOTES.txt
new file mode 100644
index 0000000..ef4cadb
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/NOTES.txt
@@ -0,0 +1,25 @@
+Redis can be accessed via port {{ .Values.redis.port }} and Sentinel can be accessed via port {{ .Values.sentinel.port }} on the following DNS name from within your cluster:
+{{ template "redis-ha.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
+
+To connect to your Redis server:
+
+{{- if .Values.auth }}
+1. To retrieve the redis password:
+ echo $(kubectl get secret {{ template "redis-ha.fullname" . }} -o "jsonpath={.data['auth']}" | base64 --decode)
+
+2. Connect to the Redis master pod that you can use as a client. By default the {{ template "redis-ha.fullname" . }}-server-0 pod is configured as the master:
+
+ kubectl exec -it {{ template "redis-ha.fullname" . }}-server-0 sh -n {{ .Release.Namespace }}
+
+3. Connect using the Redis CLI (inside container):
+
+ redis-cli -a
+{{- else }}
+1. Run a Redis pod that you can use as a client:
+
+ kubectl exec -it {{ template "redis-ha.fullname" . }}-server-0 sh -n {{ .Release.Namespace }}
+
+2. Connect using the Redis CLI:
+
+ redis-cli -h {{ template "redis-ha.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
+{{- end }}
diff --git a/manifests/redis-cluster/redis-ha/templates/_helpers.tpl b/manifests/redis-cluster/redis-ha/templates/_helpers.tpl
new file mode 100644
index 0000000..166956e
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/_helpers.tpl
@@ -0,0 +1,53 @@
+{{/* vim: set filetype=mustache: */}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "redis-ha.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "redis-ha.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{- /*
+Credit: @technosophos
+https://github.com/technosophos/common-chart/
+labels.standard prints the standard Helm labels.
+The standard labels are frequently used in metadata.
+*/ -}}
+{{- define "labels.standard" -}}
+app: {{ template "redis-ha.name" . }}
+heritage: {{ .Release.Service | quote }}
+release: {{ .Release.Name | quote }}
+chart: {{ template "chartref" . }}
+{{- end -}}
+
+{{- /*
+Credit: @technosophos
+https://github.com/technosophos/common-chart/
+chartref prints a chart name and version.
+It does minimal escaping for use in Kubernetes labels.
+Example output:
+ zookeeper-1.2.3
+ wordpress-3.2.1_20170219
+*/ -}}
+{{- define "chartref" -}}
+ {{- replace "+" "_" .Chart.Version | printf "%s-%s" .Chart.Name -}}
+{{- end -}}
+
diff --git a/manifests/redis-cluster/redis-ha/templates/redis-auth-secret.yaml b/manifests/redis-cluster/redis-ha/templates/redis-auth-secret.yaml
new file mode 100644
index 0000000..3f01a68
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/redis-auth-secret.yaml
@@ -0,0 +1,11 @@
+{{- if and .Values.auth (not .Values.existingSecret) -}}
+apiVersion: v1
+kind: Secret
+metadata:
+ name: {{ template "redis-ha.fullname" . }}
+ labels:
+{{ include "labels.standard" . | indent 4 }}
+type: Opaque
+data:
+ auth: {{ .Values.redisPassword | b64enc | quote }}
+{{- end -}}
diff --git a/manifests/redis-cluster/redis-ha/templates/redis-ha-announce-service.yaml b/manifests/redis-cluster/redis-ha/templates/redis-ha-announce-service.yaml
new file mode 100644
index 0000000..160fd64
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/redis-ha-announce-service.yaml
@@ -0,0 +1,33 @@
+{{- $fullName := include "redis-ha.fullname" . }}
+{{- $replicas := int .Values.replicas }}
+{{- $root := . }}
+{{- range $i := until $replicas }}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ $fullName }}-announce-{{ $i }}
+ labels:
+{{ include "labels.standard" $root | indent 4 }}
+ annotations:
+ service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
+ {{- if $root.Values.serviceAnnotations }}
+{{ toYaml $root.Values.serviceAnnotations | indent 4 }}
+ {{- end }}
+spec:
+ publishNotReadyAddresses: true
+ type: ClusterIP
+ ports:
+ - name: server
+ port: {{ $root.Values.redis.port }}
+ protocol: TCP
+ targetPort: redis
+ - name: sentinel
+ port: {{ $root.Values.sentinel.port }}
+ protocol: TCP
+ targetPort: sentinel
+ selector:
+ release: {{ $root.Release.Name }}
+ app: {{ include "redis-ha.name" $root }}
+ "statefulset.kubernetes.io/pod-name": {{ $fullName }}-server-{{ $i }}
+{{- end }}
diff --git a/manifests/redis-cluster/redis-ha/templates/redis-ha-configmap.yaml b/manifests/redis-cluster/redis-ha/templates/redis-ha-configmap.yaml
new file mode 100644
index 0000000..b77875d
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/redis-ha-configmap.yaml
@@ -0,0 +1,137 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "redis-ha.fullname" . }}-configmap
+ labels:
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ app: {{ template "redis-ha.fullname" . }}
+data:
+ redis.conf: |
+{{- if .Values.redis.customConfig }}
+{{ .Values.redis.customConfig | indent 4 }}
+{{- else }}
+ dir "/data"
+ {{- range $key, $value := .Values.redis.config }}
+ {{ $key }} {{ $value }}
+ {{- end }}
+{{- if .Values.auth }}
+ requirepass replace-default-auth
+ masterauth replace-default-auth
+{{- end }}
+{{- end }}
+
+ sentinel.conf: |
+{{- if .Values.sentinel.customConfig }}
+{{ .Values.sentinel.customConfig | indent 4 }}
+{{- else }}
+ dir "/data"
+ {{- $root := . -}}
+ {{- range $key, $value := .Values.sentinel.config }}
+ sentinel {{ $key }} {{ $root.Values.redis.masterGroupName }} {{ $value }}
+ {{- end }}
+{{- if .Values.auth }}
+ sentinel auth-pass {{ .Values.redis.masterGroupName }} replace-default-auth
+{{- end }}
+{{- end }}
+
+ init.sh: |
+ HOSTNAME="$(hostname)"
+ INDEX="${HOSTNAME##*-}"
+ MASTER="$(redis-cli -h {{ template "redis-ha.fullname" . }} -p {{ .Values.sentinel.port }} sentinel get-master-addr-by-name {{ .Values.redis.masterGroupName }} | grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')"
+ MASTER_GROUP="{{ .Values.redis.masterGroupName }}"
+ QUORUM="{{ .Values.sentinel.quorum }}"
+ REDIS_CONF=/data/conf/redis.conf
+ REDIS_PORT={{ .Values.redis.port }}
+ SENTINEL_CONF=/data/conf/sentinel.conf
+ SENTINEL_PORT={{ .Values.sentinel.port }}
+ SERVICE={{ template "redis-ha.fullname" . }}
+ set -eu
+
+ sentinel_update() {
+ echo "Updating sentinel config"
+ sed -i "1s/^/$(cat sentinel-id)\\n/" "$SENTINEL_CONF"
+ sed -i "2s/^/sentinel monitor $MASTER_GROUP $1 $REDIS_PORT $QUORUM \\n/" "$SENTINEL_CONF"
+ echo "sentinel announce-ip $ANNOUNCE_IP" >> $SENTINEL_CONF
+ echo "sentinel announce-port $SENTINEL_PORT" >> $SENTINEL_CONF
+ }
+
+ redis_update() {
+ echo "Updating redis config"
+ echo "slaveof $1 $REDIS_PORT" >> "$REDIS_CONF"
+ echo "slave-announce-ip $ANNOUNCE_IP" >> $REDIS_CONF
+ echo "slave-announce-port $REDIS_PORT" >> $REDIS_CONF
+ }
+
+ copy_config() {
+ if [ -f "$SENTINEL_CONF" ]; then
+ grep "sentinel myid" "$SENTINEL_CONF" > sentinel-id || true
+ fi
+ cp /readonly-config/redis.conf "$REDIS_CONF"
+ cp /readonly-config/sentinel.conf "$SENTINEL_CONF"
+ }
+
+ setup_defaults() {
+ echo "Setting up defaults"
+ if [ "$INDEX" = "0" ]; then
+ echo "Setting this pod as the default master"
+ sed -i "s/^.*slaveof.*//" "$REDIS_CONF"
+ sentinel_update "$ANNOUNCE_IP"
+ else
+ DEFAULT_MASTER="$(getent hosts "$SERVICE-announce-0" | awk '{ print $1 }')"
+ if [ -z "$DEFAULT_MASTER" ]; then
+ echo "Unable to resolve host"
+ exit 1
+ fi
+ echo "Setting default slave config.."
+ redis_update "$DEFAULT_MASTER"
+ sentinel_update "$DEFAULT_MASTER"
+ fi
+ }
+
+ find_master() {
+ echo "Attempting to find master"
+ if [ "$(redis-cli -h "$MASTER"{{ if .Values.auth }} -a "$AUTH"{{ end }} ping)" != "PONG" ]; then
+ echo "Can't ping master, attempting to force failover"
+ if redis-cli -h "$SERVICE" -p "$SENTINEL_PORT" sentinel failover "$MASTER_GROUP" | grep -q 'NOGOODSLAVE' ; then
+ setup_defaults
+ return 0
+ fi
+ sleep 10
+ MASTER="$(redis-cli -h $SERVICE -p $SENTINEL_PORT sentinel get-master-addr-by-name $MASTER_GROUP | grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')"
+ if [ "$MASTER" ]; then
+ sentinel_update "$MASTER"
+ redis_update "$MASTER"
+ else
+ echo "Could not failover, exiting..."
+ exit 1
+ fi
+ else
+ echo "Found reachable master, updating config"
+ sentinel_update "$MASTER"
+ redis_update "$MASTER"
+ fi
+ }
+
+ mkdir -p /data/conf/
+
+ echo "Initializing config.."
+ copy_config
+
+ ANNOUNCE_IP=$(getent hosts "$SERVICE-announce-$INDEX" | awk '{ print $1 }')
+ if [ -z "$ANNOUNCE_IP" ]; then
+ "Could not resolve the announce ip for this pod"
+ exit 1
+ elif [ "$MASTER" ]; then
+ find_master
+ else
+ setup_defaults
+ fi
+
+ if [ "${AUTH:-}" ]; then
+ echo "Setting auth values"
+ sed -i "s/replace-default-auth/$AUTH/" "$REDIS_CONF" "$SENTINEL_CONF"
+ fi
+
+ echo "Ready..."
diff --git a/manifests/redis-cluster/redis-ha/templates/redis-ha-healthchecks.yaml b/manifests/redis-cluster/redis-ha/templates/redis-ha-healthchecks.yaml
new file mode 100644
index 0000000..bef98ae
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/redis-ha-healthchecks.yaml
@@ -0,0 +1,41 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "redis-ha.fullname" . }}-probes
+ labels:
+ heritage: {{ .Release.Service }}
+ release: {{ .Release.Name }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version }}
+ app: {{ template "redis-ha.fullname" . }}
+data:
+ check-quorum.sh: |
+ #!/bin/sh
+ set -eu
+ MASTER_GROUP="{{ .Values.redis.masterGroupName }}"
+ SENTINEL_PORT={{ .Values.sentinel.port }}
+ REDIS_PORT={{ .Values.redis.port }}
+ NUM_SLAVES=$(redis-cli -p "$SENTINEL_PORT" sentinel master {{ .Values.redis.masterGroupName }} | awk '/num-slaves/{getline; print}')
+ MIN_SLAVES={{ index .Values.redis.config "min-slaves-to-write" }}
+
+ if [ "$1" = "$SENTINEL_PORT" ]; then
+ if redis-cli -p "$SENTINEL_PORT" sentinel ckquorum "$MASTER_GROUP" | grep -q NOQUORUM ; then
+ echo "ERROR: NOQUORUM. Sentinel quorum check failed, not enough sentinels found"
+ exit 1
+ fi
+ elif [ "$1" = "$REDIS_PORT" ]; then
+ if [ "$MIN_SLAVES" -gt "$NUM_SLAVES" ]; then
+ echo "Could not find enough replicating slaves. Needed $MIN_SLAVES but found $NUM_SLAVES"
+ exit 1
+ fi
+ fi
+ sh /probes/readiness.sh "$1"
+
+ readiness.sh: |
+ #!/bin/sh
+ set -eu
+ CHECK_SERVER="$(redis-cli -p "$1"{{ if .Values.auth }} -a "$AUTH"{{ end }} ping)"
+
+ if [ "$CHECK_SERVER" != "PONG" ]; then
+ echo "Server check failed with: $CHECK_SERVER"
+ exit 1
+ fi
diff --git a/manifests/redis-cluster/redis-ha/templates/redis-ha-pdb.yaml b/manifests/redis-cluster/redis-ha/templates/redis-ha-pdb.yaml
new file mode 100644
index 0000000..28697c3
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/redis-ha-pdb.yaml
@@ -0,0 +1,14 @@
+{{- if .Values.podDisruptionBudget -}}
+apiVersion: policy/v1beta1
+kind: PodDisruptionBudget
+metadata:
+ name: {{ template "redis-ha.fullname" . }}-pdb
+ labels:
+{{ include "labels.standard" . | indent 4 }}
+spec:
+ selector:
+ matchLabels:
+ release: {{ .Release.Name }}
+ app: {{ template "redis-ha.name" . }}
+{{ toYaml .Values.podDisruptionBudget | indent 2 }}
+{{- end -}}
diff --git a/manifests/redis-cluster/redis-ha/templates/redis-ha-service.yaml b/manifests/redis-cluster/redis-ha/templates/redis-ha-service.yaml
new file mode 100644
index 0000000..388bde8
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/redis-ha-service.yaml
@@ -0,0 +1,25 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ template "redis-ha.fullname" . }}
+ labels:
+{{ include "labels.standard" . | indent 4 }}
+ annotations:
+ {{- if .Values.serviceAnnotations }}
+{{ toYaml .Values.serviceAnnotations | indent 4 }}
+ {{- end }}
+spec:
+ type: ClusterIP
+ clusterIP: None
+ ports:
+ - name: server
+ port: {{ .Values.redis.port }}
+ protocol: TCP
+ targetPort: redis
+ - name: sentinel
+ port: {{ .Values.sentinel.port }}
+ protocol: TCP
+ targetPort: sentinel
+ selector:
+ release: {{ .Release.Name }}
+ app: {{ template "redis-ha.name" . }}
diff --git a/manifests/redis-cluster/redis-ha/templates/redis-ha-statefulset.yaml b/manifests/redis-cluster/redis-ha/templates/redis-ha-statefulset.yaml
new file mode 100644
index 0000000..cc05c82
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/redis-ha-statefulset.yaml
@@ -0,0 +1,183 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: {{ template "redis-ha.fullname" . }}-server
+ labels:
+{{ include "labels.standard" . | indent 4 }}
+spec:
+ selector:
+ matchLabels:
+ release: {{ .Release.Name }}
+ app: {{ template "redis-ha.name" . }}
+ serviceName: {{ template "redis-ha.fullname" . }}
+ replicas: {{ .Values.replicas }}
+ podManagementPolicy: OrderedReady
+ updateStrategy:
+ type: RollingUpdate
+ template:
+ metadata:
+ annotations:
+ checksum/init-config: {{ include (print $.Template.BasePath "/redis-ha-configmap.yaml") . | sha256sum }}
+ checksum/probe-config: {{ include (print $.Template.BasePath "/redis-ha-healthchecks.yaml") . | sha256sum }}
+ {{- if .Values.podAnnotations }}
+{{ toYaml .Values.podAnnotations | indent 8 }}
+ {{- end }}
+ labels:
+ release: {{ .Release.Name }}
+ app: {{ template "redis-ha.name" . }}
+ spec:
+ {{- if .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.nodeSelector | indent 8 }}
+ {{- end }}
+ {{- if .Values.tolerations }}
+ tolerations:
+{{ toYaml .Values.tolerations | indent 8 }}
+ {{- end }}
+ {{- with .Values.affinity }}
+ affinity:
+{{ tpl . $ | indent 8 }}
+ {{- end }}
+ securityContext:
+{{ toYaml .Values.securityContext | indent 8 }}
+ initContainers:
+ - name: config-init
+ image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ resources:
+{{ toYaml .Values.init.resources | indent 10 }}
+ command:
+ - sh
+ args:
+ - /readonly-config/init.sh
+{{- if .Values.auth }}
+ env:
+ - name: AUTH
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "redis-ha.fullname" . }}
+ {{- end }}
+ key: auth
+{{- end }}
+ volumeMounts:
+ - name: config
+ mountPath: /readonly-config
+ readOnly: true
+ - name: data
+ mountPath: /data
+ containers:
+ - name: redis
+ image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ command:
+ - redis-server
+ args:
+ - /data/conf/redis.conf
+{{- if .Values.auth }}
+ env:
+ - name: AUTH
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "redis-ha.fullname" . }}
+ {{- end }}
+ key: auth
+{{- end }}
+ livenessProbe:
+ exec:
+ command: [ "sh", "/probes/readiness.sh", "{{ .Values.redis.port }}"]
+ initialDelaySeconds: 15
+ periodSeconds: 5
+ readinessProbe:
+ exec:
+ command: ["sh", "/probes/readiness.sh", "{{ .Values.redis.port }}"]
+ initialDelaySeconds: 15
+ periodSeconds: 5
+ resources:
+{{ toYaml .Values.redis.resources | indent 10 }}
+ ports:
+ - name: redis
+ containerPort: {{ .Values.redis.port }}
+ volumeMounts:
+ - mountPath: /data
+ name: data
+ - mountPath: /probes
+ name: probes
+ - name: sentinel
+ image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ command:
+ - redis-sentinel
+ args:
+ - /data/conf/sentinel.conf
+{{- if .Values.auth }}
+ env:
+ - name: AUTH
+ valueFrom:
+ secretKeyRef:
+ {{- if .Values.existingSecret }}
+ name: {{ .Values.existingSecret }}
+ {{- else }}
+ name: {{ template "redis-ha.fullname" . }}
+ {{- end }}
+ key: auth
+{{- end }}
+ livenessProbe:
+ exec:
+ command: [ "sh", "/probes/readiness.sh", "{{ .Values.sentinel.port }}"]
+ initialDelaySeconds: 15
+ periodSeconds: 5
+ readinessProbe:
+ exec:
+ command: ["sh", "/probes/readiness.sh", "{{ .Values.sentinel.port }}"]
+ initialDelaySeconds: 15
+ periodSeconds: 5
+ resources:
+{{ toYaml .Values.sentinel.resources | indent 10 }}
+ ports:
+ - name: sentinel
+ containerPort: {{ .Values.sentinel.port }}
+ volumeMounts:
+ - mountPath: /data
+ name: data
+ - mountPath: /probes
+ name: probes
+ volumes:
+ - name: config
+ configMap:
+ name: {{ template "redis-ha.fullname" . }}-configmap
+ - name: probes
+ configMap:
+ name: {{ template "redis-ha.fullname" . }}-probes
+{{- if .Values.persistentVolume.enabled }}
+ volumeClaimTemplates:
+ - metadata:
+ name: data
+ annotations:
+ {{- range $key, $value := .Values.persistentVolume.annotations }}
+ {{ $key }}: {{ $value }}
+ {{- end }}
+ spec:
+ accessModes:
+ {{- range .Values.persistentVolume.accessModes }}
+ - {{ . | quote }}
+ {{- end }}
+ resources:
+ requests:
+ storage: {{ .Values.persistentVolume.size | quote }}
+ {{- if .Values.persistentVolume.storageClass }}
+ {{- if (eq "-" .Values.persistentVolume.storageClass) }}
+ storageClassName: ""
+ {{- else }}
+ storageClassName: "{{ .Values.persistentVolume.storageClass }}"
+ {{- end }}
+ {{- end }}
+{{- else }}
+ - name: data
+ emptyDir: {}
+{{- end }}
diff --git a/manifests/redis-cluster/redis-ha/templates/tests/test-redis-ha-configmap.yaml b/manifests/redis-cluster/redis-ha/templates/tests/test-redis-ha-configmap.yaml
new file mode 100644
index 0000000..5101b5d
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/tests/test-redis-ha-configmap.yaml
@@ -0,0 +1,36 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: {{ template "redis-ha.fullname" . }}-configmap-test
+ labels:
+{{ include "labels.standard" . | indent 4 }}
+ annotations:
+ "helm.sh/hook": test-success
+spec:
+ containers:
+ - name: check-init
+ image: koalaman/shellcheck:v0.5.0
+ args:
+ - --shell=sh
+ - /readonly-config/init.sh
+ volumeMounts:
+ - name: config
+ mountPath: /readonly-config
+ readOnly: true
+ - name: check-probes
+ image: koalaman/shellcheck:v0.5.0
+ args:
+ - --shell=sh
+ - /probes/check-quorum.sh
+ volumeMounts:
+ - name: probes
+ mountPath: /probes
+ readOnly: true
+ volumes:
+ - name: config
+ configMap:
+ name: {{ template "redis-ha.fullname" . }}-configmap
+ - name: probes
+ configMap:
+ name: {{ template "redis-ha.fullname" . }}-probes
+ restartPolicy: Never
diff --git a/manifests/redis-cluster/redis-ha/templates/tests/test-redis-ha-service.yaml b/manifests/redis-cluster/redis-ha/templates/tests/test-redis-ha-service.yaml
new file mode 100644
index 0000000..d22058b
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/templates/tests/test-redis-ha-service.yaml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: {{ template "redis-ha.fullname" . }}-service-test
+ labels:
+{{ include "labels.standard" . | indent 4 }}
+ annotations:
+ "helm.sh/hook": test-success
+spec:
+ containers:
+ - name: "{{ .Release.Name }}-service-test"
+ image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
+ command:
+ - sh
+ - -c
+ - redis-cli -h {{ template "redis-ha.fullname" . }} -p {{ .Values.redis.port }} info server
+ restartPolicy: Never
diff --git a/manifests/redis-cluster/redis-ha/values.yaml b/manifests/redis-cluster/redis-ha/values.yaml
new file mode 100644
index 0000000..092146b
--- /dev/null
+++ b/manifests/redis-cluster/redis-ha/values.yaml
@@ -0,0 +1,120 @@
+## Configure resource requests and limits
+## ref: http://kubernetes.io/docs/user-guide/compute-resources/
+##
+image:
+ repository: redis
+ tag: 5.0.3-alpine
+ pullPolicy: IfNotPresent
+## replicas number for each component
+replicas: 3
+
+## Redis specific configuration options
+redis:
+ port: 6379
+ masterGroupName: mymaster
+ config:
+ ## Additional redis conf options can be added below
+ ## For all available options see http://download.redis.io/redis-stable/redis.conf
+ min-slaves-to-write: 1
+ min-slaves-max-lag: 5 # Value in seconds
+ maxmemory: "0" # Max memory to use for each redis instance. Default is unlimited.
+ maxmemory-policy: "volatile-lru" # Max memory policy to use for each redis instance. Default is volatile-lru.
+ # Determines if scheduled RDB backups are created. Default is false.
+ # Please note that local (on-disk) RDBs will still be created when re-syncing with a new slave. The only way to prevent this is to enable diskless replication.
+ save: "900 1"
+ # When enabled, directly sends the RDB over the wire to slaves, without using the disk as intermediate storage. Default is false.
+ repl-diskless-sync: "yes"
+ rdbcompression: "yes"
+ rdbchecksum: "yes"
+
+ ## Custom redis.conf files used to override default settings. If this file is
+ ## specified then the redis.config above will be ignored.
+ # customConfig: |-
+ # Define configuration here
+
+ resources: {}
+ # requests:
+ # memory: 200Mi
+ # cpu: 100m
+ # limits:
+ # memory: 700Mi
+
+## Sentinel specific configuration options
+sentinel:
+ port: 26379
+ quorum: 2
+ config:
+ ## Additional sentinel conf options can be added below. Only options that
+ ## are expressed in the format simialar to 'sentinel xxx mymaster xxx' will
+ ## be properly templated.
+ ## For available options see http://download.redis.io/redis-stable/sentinel.conf
+ down-after-milliseconds: 10000
+ ## Failover timeout value in milliseconds
+ failover-timeout: 180000
+ parallel-syncs: 5
+
+ ## Custom sentinel.conf files used to override default settings. If this file is
+ ## specified then the sentinel.config above will be ignored.
+ # customConfig: |-
+ # Define configuration here
+
+ resources: {}
+ # requests:
+ # memory: 200Mi
+ # cpu: 100m
+ # limits:
+ # memory: 200Mi
+
+securityContext:
+ runAsUser: 1000
+ fsGroup: 1000
+ runAsNonRoot: true
+
+## Node labels, affinity, and tolerations for pod assignment
+## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
+## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature
+## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
+affinity: |
+ podAntiAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - labelSelector:
+ matchLabels:
+ app: {{ template "redis-ha.name" . }}
+ release: {{ .Release.Name }}
+ topologyKey: kubernetes.io/hostname
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - weight: 100
+ podAffinityTerm:
+ labelSelector:
+ matchLabels:
+ app: {{ template "redis-ha.name" . }}
+ release: {{ .Release.Name }}
+ topologyKey: failure-domain.beta.kubernetes.io/zone
+
+podDisruptionBudget: {}
+ # maxUnavailable: 1
+ # minAvailable: 1
+
+## Configures redis with AUTH (requirepass & masterauth conf params)
+auth: false
+# redisPassword:
+
+## Use existing secret containing "auth" key (ignores redisPassword)
+# existingSecret:
+
+persistentVolume:
+ enabled: true
+ ## redis-ha data Persistent Volume Storage Class
+ ## If defined, storageClassName:
+ ## If set to "-", storageClassName: "", which disables dynamic provisioning
+ ## If undefined (the default) or set to null, no storageClassName spec is
+ ## set, choosing the default provisioner. (gp2 on AWS, standard on
+ ## GKE, AWS & OpenStack)
+ ##
+ # storageClass: "-"
+ accessModes:
+ - ReadWriteOnce
+ size: 10Gi
+ annotations: {}
+init:
+ resources: {}
diff --git a/manifests/storage/test.yaml b/manifests/storage/test.yaml
new file mode 100644
index 0000000..c1c361e
--- /dev/null
+++ b/manifests/storage/test.yaml
@@ -0,0 +1,35 @@
+kind: PersistentVolumeClaim
+apiVersion: v1
+metadata:
+ name: test-claim
+spec:
+ storageClassName: nfs-dynamic-class-01
+ accessModes:
+ - ReadWriteMany
+ resources:
+ requests:
+ storage: 1Mi
+
+---
+kind: Pod
+apiVersion: v1
+metadata:
+ name: test
+spec:
+ containers:
+ - name: test
+ image: busybox:1.28.4
+ imagePullPolicy: IfNotPresent
+ command:
+ - "/bin/sh"
+ args:
+ - "-c"
+ - "echo 'hello k8s' > /mnt/SUCCESS && sleep 36000 || exit 1"
+ volumeMounts:
+ - name: nfs-pvc
+ mountPath: "/mnt"
+ restartPolicy: "Never"
+ volumes:
+ - name: nfs-pvc
+ persistentVolumeClaim:
+ claimName: test-claim
diff --git a/pics/alipay.gif b/pics/alipay.gif
new file mode 100644
index 0000000..ef8f663
Binary files /dev/null and b/pics/alipay.gif differ
diff --git a/pics/allow_from_external.gif b/pics/allow_from_external.gif
new file mode 100644
index 0000000..ea86d5e
Binary files /dev/null and b/pics/allow_from_external.gif differ
diff --git a/pics/cilium_http_gsg.jpg b/pics/cilium_http_gsg.jpg
new file mode 100644
index 0000000..a95885b
Binary files /dev/null and b/pics/cilium_http_gsg.jpg differ
diff --git a/pics/cilium_http_l3_l4_gsg.jpg b/pics/cilium_http_l3_l4_gsg.jpg
new file mode 100644
index 0000000..f8e9d3a
Binary files /dev/null and b/pics/cilium_http_l3_l4_gsg.jpg differ
diff --git a/pics/cilium_http_l3_l4_l7_gsg.jpg b/pics/cilium_http_l3_l4_l7_gsg.jpg
new file mode 100644
index 0000000..6c6ee80
Binary files /dev/null and b/pics/cilium_http_l3_l4_l7_gsg.jpg differ
diff --git a/pics/deny_from_other_namespaces.gif b/pics/deny_from_other_namespaces.gif
new file mode 100644
index 0000000..2dd0d1e
Binary files /dev/null and b/pics/deny_from_other_namespaces.gif differ
diff --git a/pics/grafana.png b/pics/grafana.png
new file mode 100644
index 0000000..7274e91
Binary files /dev/null and b/pics/grafana.png differ
diff --git a/pics/influxdb.png b/pics/influxdb.png
new file mode 100644
index 0000000..7fcd978
Binary files /dev/null and b/pics/influxdb.png differ
diff --git a/pics/wxpay.gif b/pics/wxpay.gif
new file mode 100644
index 0000000..362abc0
Binary files /dev/null and b/pics/wxpay.gif differ
diff --git a/roles/calico/defaults/main.yml b/roles/calico/defaults/main.yml
new file mode 100644
index 0000000..5ead263
--- /dev/null
+++ b/roles/calico/defaults/main.yml
@@ -0,0 +1,28 @@
+# 部分calico相关配置,更全配置可以去roles/calico/templates/calico.yaml.j2自定义
+
+# 如果 node 节点有多块网卡,请设置 true
+# 另外发现设置为 true 时能够解决v1.10使用ipvs偶尔出现pod内‘dial tcp 10.68.0.1:443: i/o timeout’的 bug
+NODE_WITH_MULTIPLE_NETWORKS: "true"
+
+# etcd 集群服务地址列表, 根据etcd组成员自动生成
+TMP_ENDPOINTS: "{% for h in groups['etcd'] %}https://{{ h }}:2379,{% endfor %}"
+ETCD_ENDPOINTS: "{{ TMP_ENDPOINTS.rstrip(',') }}"
+
+# 设置 CALICO_IPV4POOL_IPIP=“off”,可以提高网络性能,条件限制详见 docs/setup/calico.md
+CALICO_IPV4POOL_IPIP: "Always"
+
+# 设置 Felix 日志级别
+FELIX_LOG_LVL: "warning"
+
+# 设置 calico-node使用的host IP,bgp邻居通过该地址建立,可手工指定也可以自动发现
+#IP_AUTODETECTION_METHOD: "interface=eth0"
+IP_AUTODETECTION_METHOD: "can-reach={{ groups.deploy[0] }}"
+
+# 更新支持calico 版本: [v3.2.x] [v3.3.x] [v3.4.x]
+calico_ver: "v3.4.1"
+
+# calico 主版本
+calico_ver_main: "{{ calico_ver.split('.')[0] }}.{{ calico_ver.split('.')[1] }}"
+
+# 离线镜像tar包
+calico_offline: "calico_{{ calico_ver }}.tar"
diff --git a/roles/calico/tasks/main.yml b/roles/calico/tasks/main.yml
new file mode 100644
index 0000000..7136e42
--- /dev/null
+++ b/roles/calico/tasks/main.yml
@@ -0,0 +1,108 @@
+- block:
+ - name: 在deploy 节点创建相关目录
+ file: name={{ item }} state=directory
+ with_items:
+ - /etc/calico/ssl
+ - /opt/kube/kube-system/calico
+
+ - name: 创建calico 证书请求
+ template: src=calico-csr.json.j2 dest=/etc/calico/ssl/calico-csr.json
+
+ - name: 创建 calico证书和私钥
+ shell: "cd /etc/calico/ssl && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes calico-csr.json | {{ bin_dir }}/cfssljson -bare calico"
+
+ - name: get calico-etcd-secrets info
+ shell: "{{ bin_dir }}/kubectl get secrets -n kube-system"
+ register: secrets_info
+
+ - name: 创建 calico-etcd-secrets
+ shell: "cd /etc/calico/ssl && \
+ {{ bin_dir }}/kubectl create secret generic -n kube-system calico-etcd-secrets \
+ --from-file=etcd-ca={{ ca_dir }}/ca.pem \
+ --from-file=etcd-key=calico-key.pem \
+ --from-file=etcd-cert=calico.pem"
+ when: '"calico-etcd-secrets" not in secrets_info.stdout'
+
+ - name: 配置 calico DaemonSet yaml文件
+ template: src=calico-{{ calico_ver_main }}.yaml.j2 dest=/opt/kube/kube-system/calico/calico.yaml
+
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+- name: 检查是否已下载离线calico镜像
+ command: "ls {{ base_dir }}/down"
+ register: download_info
+ connection: local
+ run_once: true
+
+- name: node 节点创建calico 相关目录
+ file: name={{ item }} state=directory
+ with_items:
+ - /etc/calico/ssl
+ - /etc/cni/net.d
+ - /opt/kube/images
+
+# 【可选】推送离线docker 镜像,可以忽略执行错误
+- name: 尝试推送离线docker 镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ item }} dest=/opt/kube/images/{{ item }}
+ when: 'item in download_info.stdout'
+ with_items:
+ - "pause_3.1.tar"
+ - "{{ calico_offline }}"
+ ignore_errors: true
+
+- name: 获取calico离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+# 如果目录下有离线镜像,就把它导入到node节点上
+- name: 导入 calico的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ item }}"
+ when: 'item in image_info.stdout'
+ with_items:
+ - "pause_3.1.tar"
+ - "{{ calico_offline }}"
+ ignore_errors: true
+
+# 只需单节点执行一次
+- name: 运行 calico网络
+ shell: "{{ bin_dir }}/kubectl apply -f /opt/kube/kube-system/calico/ && sleep 5"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+# 删除原有cni配置
+- name: 删除默认cni配置
+ file: path=/etc/cni/net.d/10-default.conf state=absent
+
+# [可选]cni calico plugins 已经在calico.yaml完成自动安装
+- name: 下载calicoctl 客户端
+ copy: src={{ base_dir }}/bin/{{ item }} dest={{ bin_dir }}/{{ item }} mode=0755
+ with_items:
+ #- calico
+ #- calico-ipam
+ #- loopback
+ - calicoctl
+
+- name: 分发 calico 证书
+ synchronize: src=/etc/calico/ssl/{{ item }} dest=/etc/calico/ssl/{{ item }}
+ with_items:
+ - calico.pem
+ - calico-key.pem
+ delegate_to: "{{ groups.deploy[0] }}"
+
+- name: 准备 calicoctl配置文件
+ template: src=calicoctl.cfg.j2 dest=/etc/calico/calicoctl.cfg
+
+# 等待网络插件部署成功,视下载镜像速度而定
+- name: 轮询等待calico-node 运行,视下载镜像速度而定
+ shell: "{{ bin_dir }}/kubectl get pod -n kube-system -o wide|grep 'calico-node'|grep ' {{ inventory_hostname }} '|awk '{print $3}'"
+ register: pod_status
+ until: pod_status.stdout == "Running"
+ delegate_to: "{{ groups.deploy[0] }}"
+ retries: 15
+ delay: 15
+ ignore_errors: true
diff --git a/roles/calico/templates/calico-csr.json.j2 b/roles/calico/templates/calico-csr.json.j2
new file mode 100644
index 0000000..b3dd1ef
--- /dev/null
+++ b/roles/calico/templates/calico-csr.json.j2
@@ -0,0 +1,17 @@
+{
+ "CN": "calico",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/calico/templates/calico-v3.2.yaml.j2 b/roles/calico/templates/calico-v3.2.yaml.j2
new file mode 100644
index 0000000..13c8584
--- /dev/null
+++ b/roles/calico/templates/calico-v3.2.yaml.j2
@@ -0,0 +1,469 @@
+# Calico Version {{ calico_ver }}
+# https://docs.projectcalico.org/v3.2/releases#{{ calico_ver }}
+# This manifest includes the following component versions:
+# calico/node:{{ calico_ver }}
+# calico/cni:{{ calico_ver }}
+# calico/kube-controllers:{{ calico_ver }}
+
+# This ConfigMap is used to configure a self-hosted Calico installation.
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: calico-config
+ namespace: kube-system
+data:
+ # Configure this with the location of your etcd cluster.
+ etcd_endpoints: "{{ ETCD_ENDPOINTS }}"
+
+ # If you're using TLS enabled etcd uncomment the following.
+ # You must also populate the Secret below with these files.
+ etcd_ca: "/calico-secrets/etcd-ca"
+ etcd_cert: "/calico-secrets/etcd-cert"
+ etcd_key: "/calico-secrets/etcd-key"
+ # Configure the Calico backend to use.
+ calico_backend: "bird"
+
+ # Configure the MTU to use
+ veth_mtu: "1440"
+
+ # The CNI network configuration to install on each node.
+ cni_network_config: |-
+ {
+ "name": "k8s-pod-network",
+ "cniVersion": "0.3.0",
+ "plugins": [
+ {
+ "type": "calico",
+ "log_level": "warning",
+ "etcd_endpoints": "{{ ETCD_ENDPOINTS }}",
+ "etcd_key_file": "/etc/calico/ssl/calico-key.pem",
+ "etcd_cert_file": "/etc/calico/ssl/calico.pem",
+ "etcd_ca_cert_file": "/etc/kubernetes/ssl/ca.pem",
+ "mtu": 1500,
+ "ipam": {
+ "type": "calico-ipam"
+ },
+ "policy": {
+ "type": "k8s"
+ },
+ "kubernetes": {
+ "kubeconfig": "/root/.kube/config"
+ }
+ },
+ {
+ "type": "portmap",
+ "snat": true,
+ "capabilities": {"portMappings": true}
+ }
+ ]
+ }
+
+---
+
+# We use cmd-line-way( kubectl create) to create secrets 'calico-etcd-secrets',
+# refer to 'roles/calico/tasks/main.yml' for details.
+
+---
+
+# This manifest installs the calico/node container, as well
+# as the Calico CNI plugins and network config on
+# each master and worker node in a Kubernetes cluster.
+kind: DaemonSet
+apiVersion: extensions/v1beta1
+metadata:
+ name: calico-node
+ namespace: kube-system
+ labels:
+ k8s-app: calico-node
+spec:
+ selector:
+ matchLabels:
+ k8s-app: calico-node
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 1
+ template:
+ metadata:
+ labels:
+ k8s-app: calico-node
+ annotations:
+ # This, along with the CriticalAddonsOnly toleration below,
+ # marks the pod as a critical add-on, ensuring it gets
+ # priority scheduling and that its resources are reserved
+ # if it ever gets evicted.
+ scheduler.alpha.kubernetes.io/critical-pod: ''
+ spec:
+ nodeSelector:
+ beta.kubernetes.io/os: linux
+ hostNetwork: true
+ tolerations:
+ # Make sure calico-node gets scheduled on all nodes.
+ - effect: NoSchedule
+ operator: Exists
+ # Mark the pod as a critical add-on for rescheduling.
+ - key: CriticalAddonsOnly
+ operator: Exists
+ - effect: NoExecute
+ operator: Exists
+ serviceAccountName: calico-node
+ # Minimize downtime during a rolling upgrade or deletion; tell Kubernetes to do a "force
+ # deletion": https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods.
+ terminationGracePeriodSeconds: 0
+ containers:
+ # Runs calico/node container on each Kubernetes node. This
+ # container programs network policy and routes on each
+ # host.
+ - name: calico-node
+ image: calico/node:{{ calico_ver }}
+ env:
+ # The location of the Calico etcd cluster.
+ - name: ETCD_ENDPOINTS
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_endpoints
+ # Location of the CA certificate for etcd.
+ - name: ETCD_CA_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_ca
+ # Location of the client key for etcd.
+ - name: ETCD_KEY_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_key
+ # Location of the client certificate for etcd.
+ - name: ETCD_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_cert
+ # Set noderef for node controller.
+ - name: CALICO_K8S_NODE_REF
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.nodeName
+ # Choose the backend to use.
+ - name: CALICO_NETWORKING_BACKEND
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: calico_backend
+ # Cluster type to identify the deployment type
+ - name: CLUSTER_TYPE
+ value: "k8s,bgp"
+ # Auto-detect the BGP IP address.
+ - name: IP
+ value: "autodetect"
+ - name: IP_AUTODETECTION_METHOD
+ value: "{{ IP_AUTODETECTION_METHOD }}"
+ # Enable IPIP
+ - name: CALICO_IPV4POOL_IPIP
+ value: "{{ CALICO_IPV4POOL_IPIP }}"
+ # Set MTU for tunnel device used if ipip is enabled
+ - name: FELIX_IPINIPMTU
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: veth_mtu
+ # The default IPv4 pool to create on startup if none exists. Pod IPs will be
+ # chosen from this range. Changing this value after installation will have
+ # no effect. This should fall within `--cluster-cidr`.
+ - name: CALICO_IPV4POOL_CIDR
+ value: "{{ CLUSTER_CIDR }}"
+ # Disable file logging so `kubectl logs` works.
+ - name: CALICO_DISABLE_FILE_LOGGING
+ value: "true"
+ # Set Felix endpoint to host default action to ACCEPT.
+ - name: FELIX_DEFAULTENDPOINTTOHOSTACTION
+ value: "ACCEPT"
+ # Disable IPv6 on Kubernetes.
+ - name: FELIX_IPV6SUPPORT
+ value: "false"
+ # Set Felix logging
+ - name: FELIX_LOGSEVERITYSCREEN
+ value: "{{ FELIX_LOG_LVL }}"
+ - name: FELIX_HEALTHENABLED
+ value: "true"
+ # Set Kubernetes NodePorts: If services do use NodePorts outside Calico’s expected range,
+ # Calico will treat traffic to those ports as host traffic instead of pod traffic.
+ - name: FELIX_KUBENODEPORTRANGES
+ value: "{{ NODE_PORT_RANGE.split('-')[0] }}:{{ NODE_PORT_RANGE.split('-')[1] }}"
+ securityContext:
+ privileged: true
+ resources:
+ requests:
+ cpu: 250m
+ livenessProbe:
+ httpGet:
+ path: /liveness
+ port: 9099
+ host: localhost
+ periodSeconds: 10
+ initialDelaySeconds: 10
+ failureThreshold: 6
+ readinessProbe:
+ exec:
+ command:
+ - /bin/calico-node
+ - -bird-ready
+ - -felix-ready
+ periodSeconds: 10
+ volumeMounts:
+ - mountPath: /lib/modules
+ name: lib-modules
+ readOnly: true
+ - mountPath: /var/run/calico
+ name: var-run-calico
+ readOnly: false
+ - mountPath: /var/lib/calico
+ name: var-lib-calico
+ readOnly: false
+ - mountPath: /calico-secrets
+ name: etcd-certs
+ # This container installs the Calico CNI binaries
+ # and CNI network config file on each node.
+ - name: install-cni
+ image: calico/cni:{{ calico_ver }}
+ command: ["/install-cni.sh"]
+ env:
+ # Name of the CNI config file to create.
+ - name: CNI_CONF_NAME
+ value: "10-calico.conflist"
+ # The location of the Calico etcd cluster.
+ - name: ETCD_ENDPOINTS
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_endpoints
+ # The CNI network config to install on each node.
+ - name: CNI_NETWORK_CONFIG
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: cni_network_config
+ # CNI MTU Config variable
+ - name: CNI_MTU
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: veth_mtu
+ volumeMounts:
+ - mountPath: /host/opt/cni/bin
+ name: cni-bin-dir
+ - mountPath: /host/etc/cni/net.d
+ name: cni-net-dir
+ - mountPath: /calico-secrets
+ name: etcd-certs
+ volumes:
+ # Used by calico/node.
+ - name: lib-modules
+ hostPath:
+ path: /lib/modules
+ - name: var-run-calico
+ hostPath:
+ path: /var/run/calico
+ - name: var-lib-calico
+ hostPath:
+ path: /var/lib/calico
+ # Used to install CNI.
+ - name: cni-bin-dir
+ hostPath:
+ path: {{ bin_dir }}
+ - name: cni-net-dir
+ hostPath:
+ path: /etc/cni/net.d
+ # Mount in the etcd TLS secrets with mode 400.
+ # See https://kubernetes.io/docs/concepts/configuration/secret/
+ - name: etcd-certs
+ secret:
+ secretName: calico-etcd-secrets
+ defaultMode: 0400
+---
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: calico-node
+ namespace: kube-system
+
+---
+
+# This manifest deploys the Calico Kubernetes controllers.
+# See https://github.com/projectcalico/kube-controllers
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: calico-kube-controllers
+ namespace: kube-system
+ labels:
+ k8s-app: calico-kube-controllers
+ annotations:
+ scheduler.alpha.kubernetes.io/critical-pod: ''
+spec:
+ # The controllers can only have a single active instance.
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ k8s-app: calico-kube-controllers
+ template:
+ metadata:
+ name: calico-kube-controllers
+ namespace: kube-system
+ labels:
+ k8s-app: calico-kube-controllers
+ spec:
+ nodeSelector:
+ beta.kubernetes.io/os: linux
+ # The controllers must run in the host network namespace so that
+ # it isn't governed by policy that would prevent it from working.
+ hostNetwork: true
+ tolerations:
+ # Mark the pod as a critical add-on for rescheduling.
+ - key: CriticalAddonsOnly
+ operator: Exists
+ - key: node-role.kubernetes.io/master
+ effect: NoSchedule
+ serviceAccountName: calico-kube-controllers
+ containers:
+ - name: calico-kube-controllers
+ image: calico/kube-controllers:{{ calico_ver }}
+ env:
+ # The location of the Calico etcd cluster.
+ - name: ETCD_ENDPOINTS
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_endpoints
+ # Location of the CA certificate for etcd.
+ - name: ETCD_CA_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_ca
+ # Location of the client key for etcd.
+ - name: ETCD_KEY_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_key
+ # Location of the client certificate for etcd.
+ - name: ETCD_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_cert
+ # Choose which controllers to run.
+ - name: ENABLED_CONTROLLERS
+ value: policy,profile,workloadendpoint,node
+{% if NODE_WITH_MULTIPLE_NETWORKS == 'true' %}
+ # if hosts have multiple net interfaces, set following two ENVs
+ - name: KUBERNETES_SERVICE_HOST
+ value: "{{ MASTER_IP }}"
+ - name: KUBERNETES_SERVICE_PORT
+ value: "{{ KUBE_APISERVER.split(':')[2] }}"
+{% endif %}
+ volumeMounts:
+ # Mount in the etcd TLS secrets.
+ - mountPath: /calico-secrets
+ name: etcd-certs
+ readinessProbe:
+ exec:
+ command:
+ - /usr/bin/check-status
+ - -r
+ volumes:
+ # Mount in the etcd TLS secrets with mode 400.
+ # See https://kubernetes.io/docs/concepts/configuration/secret/
+ - name: etcd-certs
+ secret:
+ secretName: calico-etcd-secrets
+ defaultMode: 0400
+
+---
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: calico-kube-controllers
+ namespace: kube-system
+
+---
+
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: calico-kube-controllers
+rules:
+ - apiGroups:
+ - ""
+ - extensions
+ resources:
+ - pods
+ - namespaces
+ - networkpolicies
+ - nodes
+ - serviceaccounts
+ verbs:
+ - watch
+ - list
+ - apiGroups:
+ - networking.k8s.io
+ resources:
+ - networkpolicies
+ verbs:
+ - watch
+ - list
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: calico-kube-controllers
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: calico-kube-controllers
+subjects:
+- kind: ServiceAccount
+ name: calico-kube-controllers
+ namespace: kube-system
+
+---
+
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: calico-node
+rules:
+ - apiGroups: [""]
+ resources:
+ - pods
+ - nodes
+ - namespaces
+ verbs:
+ - get
+ - apiGroups: [""]
+ resources:
+ - nodes/status
+ verbs:
+ - patch
+
+---
+
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ name: calico-node
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: calico-node
+subjects:
+- kind: ServiceAccount
+ name: calico-node
+ namespace: kube-system
diff --git a/roles/calico/templates/calico-v3.3.yaml.j2 b/roles/calico/templates/calico-v3.3.yaml.j2
new file mode 100644
index 0000000..c644f15
--- /dev/null
+++ b/roles/calico/templates/calico-v3.3.yaml.j2
@@ -0,0 +1,478 @@
+# Calico Version {{ calico_ver }}
+# https://docs.projectcalico.org/v3.3/releases#{{ calico_ver }}
+# This manifest includes the following component versions:
+# calico/node:{{ calico_ver }}
+# calico/cni:{{ calico_ver }}
+# calico/kube-controllers:{{ calico_ver }}
+
+# This ConfigMap is used to configure a self-hosted Calico installation.
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: calico-config
+ namespace: kube-system
+data:
+ # Configure this with the location of your etcd cluster.
+ etcd_endpoints: "{{ ETCD_ENDPOINTS }}"
+
+ # If you're using TLS enabled etcd uncomment the following.
+ # You must also populate the Secret below with these files.
+ etcd_ca: "/calico-secrets/etcd-ca"
+ etcd_cert: "/calico-secrets/etcd-cert"
+ etcd_key: "/calico-secrets/etcd-key"
+ # Configure the Calico backend to use.
+ calico_backend: "bird"
+
+ # Configure the MTU to use
+ veth_mtu: "1440"
+
+ # The CNI network configuration to install on each node.
+ cni_network_config: |-
+ {
+ "name": "k8s-pod-network",
+ "cniVersion": "0.3.0",
+ "plugins": [
+ {
+ "type": "calico",
+ "log_level": "warning",
+ "etcd_endpoints": "{{ ETCD_ENDPOINTS }}",
+ "etcd_key_file": "/etc/calico/ssl/calico-key.pem",
+ "etcd_cert_file": "/etc/calico/ssl/calico.pem",
+ "etcd_ca_cert_file": "/etc/kubernetes/ssl/ca.pem",
+ "mtu": 1500,
+ "ipam": {
+ "type": "calico-ipam"
+ },
+ "policy": {
+ "type": "k8s"
+ },
+ "kubernetes": {
+ "kubeconfig": "/root/.kube/config"
+ }
+ },
+ {
+ "type": "portmap",
+ "snat": true,
+ "capabilities": {"portMappings": true}
+ }
+ ]
+ }
+
+---
+
+# We use cmd-line-way( kubectl create) to create secrets 'calico-etcd-secrets',
+# refer to 'roles/calico/tasks/main.yml' for details.
+
+---
+
+# This manifest installs the calico/node container, as well
+# as the Calico CNI plugins and network config on
+# each master and worker node in a Kubernetes cluster.
+kind: DaemonSet
+apiVersion: extensions/v1beta1
+metadata:
+ name: calico-node
+ namespace: kube-system
+ labels:
+ k8s-app: calico-node
+spec:
+ selector:
+ matchLabels:
+ k8s-app: calico-node
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 1
+ template:
+ metadata:
+ labels:
+ k8s-app: calico-node
+ annotations:
+ # This, along with the CriticalAddonsOnly toleration below,
+ # marks the pod as a critical add-on, ensuring it gets
+ # priority scheduling and that its resources are reserved
+ # if it ever gets evicted.
+ scheduler.alpha.kubernetes.io/critical-pod: ''
+ spec:
+ nodeSelector:
+ beta.kubernetes.io/os: linux
+ hostNetwork: true
+ tolerations:
+ # Make sure calico-node gets scheduled on all nodes.
+ - effect: NoSchedule
+ operator: Exists
+ # Mark the pod as a critical add-on for rescheduling.
+ - key: CriticalAddonsOnly
+ operator: Exists
+ - effect: NoExecute
+ operator: Exists
+ serviceAccountName: calico-node
+ # Minimize downtime during a rolling upgrade or deletion; tell Kubernetes to do a "force
+ # deletion": https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods.
+ terminationGracePeriodSeconds: 0
+ containers:
+ # Runs calico/node container on each Kubernetes node. This
+ # container programs network policy and routes on each
+ # host.
+ - name: calico-node
+ image: calico/node:{{ calico_ver }}
+ env:
+ # The location of the Calico etcd cluster.
+ - name: ETCD_ENDPOINTS
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_endpoints
+ # Location of the CA certificate for etcd.
+ - name: ETCD_CA_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_ca
+ # Location of the client key for etcd.
+ - name: ETCD_KEY_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_key
+ # Location of the client certificate for etcd.
+ - name: ETCD_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_cert
+ # Set noderef for node controller.
+ - name: CALICO_K8S_NODE_REF
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.nodeName
+ # Choose the backend to use.
+ - name: CALICO_NETWORKING_BACKEND
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: calico_backend
+ # Cluster type to identify the deployment type
+ - name: CLUSTER_TYPE
+ value: "k8s,bgp"
+ # Auto-detect the BGP IP address.
+ - name: IP
+ value: "autodetect"
+ - name: IP_AUTODETECTION_METHOD
+ value: "{{ IP_AUTODETECTION_METHOD }}"
+ # Enable IPIP
+ - name: CALICO_IPV4POOL_IPIP
+ value: "{{ CALICO_IPV4POOL_IPIP }}"
+ # Set MTU for tunnel device used if ipip is enabled
+ - name: FELIX_IPINIPMTU
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: veth_mtu
+ # The default IPv4 pool to create on startup if none exists. Pod IPs will be
+ # chosen from this range. Changing this value after installation will have
+ # no effect. This should fall within `--cluster-cidr`.
+ - name: CALICO_IPV4POOL_CIDR
+ value: "{{ CLUSTER_CIDR }}"
+ # Disable file logging so `kubectl logs` works.
+ - name: CALICO_DISABLE_FILE_LOGGING
+ value: "true"
+ # Set Felix endpoint to host default action to ACCEPT.
+ - name: FELIX_DEFAULTENDPOINTTOHOSTACTION
+ value: "ACCEPT"
+ # Disable IPv6 on Kubernetes.
+ - name: FELIX_IPV6SUPPORT
+ value: "false"
+ # Set Felix logging
+ - name: FELIX_LOGSEVERITYSCREEN
+ value: "{{ FELIX_LOG_LVL }}"
+ - name: FELIX_HEALTHENABLED
+ value: "true"
+ # Set Kubernetes NodePorts: If services do use NodePorts outside Calico’s expected range,
+ # Calico will treat traffic to those ports as host traffic instead of pod traffic.
+ - name: FELIX_KUBENODEPORTRANGES
+ value: "{{ NODE_PORT_RANGE.split('-')[0] }}:{{ NODE_PORT_RANGE.split('-')[1] }}"
+ - name: FELIX_PROMETHEUSMETRICSENABLED
+ value: "true"
+ securityContext:
+ privileged: true
+ resources:
+ requests:
+ cpu: 250m
+ livenessProbe:
+ httpGet:
+ path: /liveness
+ port: 9099
+ host: localhost
+ periodSeconds: 10
+ initialDelaySeconds: 10
+ failureThreshold: 6
+ readinessProbe:
+ exec:
+ command:
+ - /bin/calico-node
+ - -bird-ready
+ - -felix-ready
+ periodSeconds: 10
+ volumeMounts:
+ - mountPath: /lib/modules
+ name: lib-modules
+ readOnly: true
+ - mountPath: /run/xtables.lock
+ name: xtables-lock
+ readOnly: false
+ - mountPath: /var/run/calico
+ name: var-run-calico
+ readOnly: false
+ - mountPath: /var/lib/calico
+ name: var-lib-calico
+ readOnly: false
+ - mountPath: /calico-secrets
+ name: etcd-certs
+ # This container installs the Calico CNI binaries
+ # and CNI network config file on each node.
+ - name: install-cni
+ image: calico/cni:{{ calico_ver }}
+ command: ["/install-cni.sh"]
+ env:
+ # Name of the CNI config file to create.
+ - name: CNI_CONF_NAME
+ value: "10-calico.conflist"
+ # The location of the Calico etcd cluster.
+ - name: ETCD_ENDPOINTS
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_endpoints
+ # The CNI network config to install on each node.
+ - name: CNI_NETWORK_CONFIG
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: cni_network_config
+ # CNI MTU Config variable
+ - name: CNI_MTU
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: veth_mtu
+ volumeMounts:
+ - mountPath: /host/opt/cni/bin
+ name: cni-bin-dir
+ - mountPath: /host/etc/cni/net.d
+ name: cni-net-dir
+ - mountPath: /calico-secrets
+ name: etcd-certs
+ volumes:
+ # Used by calico/node.
+ - name: lib-modules
+ hostPath:
+ path: /lib/modules
+ - name: var-run-calico
+ hostPath:
+ path: /var/run/calico
+ - name: var-lib-calico
+ hostPath:
+ path: /var/lib/calico
+ - name: xtables-lock
+ hostPath:
+ path: /run/xtables.lock
+ type: FileOrCreate
+ # Used to install CNI.
+ - name: cni-bin-dir
+ hostPath:
+ path: {{ bin_dir }}
+ - name: cni-net-dir
+ hostPath:
+ path: /etc/cni/net.d
+ # Mount in the etcd TLS secrets with mode 400.
+ # See https://kubernetes.io/docs/concepts/configuration/secret/
+ - name: etcd-certs
+ secret:
+ secretName: calico-etcd-secrets
+ defaultMode: 0400
+---
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: calico-node
+ namespace: kube-system
+
+---
+
+# This manifest deploys the Calico Kubernetes controllers.
+# See https://github.com/projectcalico/kube-controllers
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: calico-kube-controllers
+ namespace: kube-system
+ labels:
+ k8s-app: calico-kube-controllers
+ annotations:
+ scheduler.alpha.kubernetes.io/critical-pod: ''
+spec:
+ # The controllers can only have a single active instance.
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ k8s-app: calico-kube-controllers
+ template:
+ metadata:
+ name: calico-kube-controllers
+ namespace: kube-system
+ labels:
+ k8s-app: calico-kube-controllers
+ spec:
+ nodeSelector:
+ beta.kubernetes.io/os: linux
+ # The controllers must run in the host network namespace so that
+ # it isn't governed by policy that would prevent it from working.
+ hostNetwork: true
+ tolerations:
+ # Mark the pod as a critical add-on for rescheduling.
+ - key: CriticalAddonsOnly
+ operator: Exists
+ - key: node-role.kubernetes.io/master
+ effect: NoSchedule
+ serviceAccountName: calico-kube-controllers
+ containers:
+ - name: calico-kube-controllers
+ image: calico/kube-controllers:{{ calico_ver }}
+ env:
+ # The location of the Calico etcd cluster.
+ - name: ETCD_ENDPOINTS
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_endpoints
+ # Location of the CA certificate for etcd.
+ - name: ETCD_CA_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_ca
+ # Location of the client key for etcd.
+ - name: ETCD_KEY_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_key
+ # Location of the client certificate for etcd.
+ - name: ETCD_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_cert
+ # Choose which controllers to run.
+ - name: ENABLED_CONTROLLERS
+ value: policy,namespace,serviceaccount,workloadendpoint,node
+{% if NODE_WITH_MULTIPLE_NETWORKS == 'true' %}
+ # if hosts have multiple net interfaces, set following two ENVs
+ - name: KUBERNETES_SERVICE_HOST
+ value: "{{ MASTER_IP }}"
+ - name: KUBERNETES_SERVICE_PORT
+ value: "{{ KUBE_APISERVER.split(':')[2] }}"
+{% endif %}
+ volumeMounts:
+ # Mount in the etcd TLS secrets.
+ - mountPath: /calico-secrets
+ name: etcd-certs
+ readinessProbe:
+ exec:
+ command:
+ - /usr/bin/check-status
+ - -r
+ volumes:
+ # Mount in the etcd TLS secrets with mode 400.
+ # See https://kubernetes.io/docs/concepts/configuration/secret/
+ - name: etcd-certs
+ secret:
+ secretName: calico-etcd-secrets
+ defaultMode: 0400
+
+---
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: calico-kube-controllers
+ namespace: kube-system
+
+---
+
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: calico-kube-controllers
+rules:
+ - apiGroups:
+ - ""
+ - extensions
+ resources:
+ - pods
+ - namespaces
+ - networkpolicies
+ - nodes
+ - serviceaccounts
+ verbs:
+ - watch
+ - list
+ - apiGroups:
+ - networking.k8s.io
+ resources:
+ - networkpolicies
+ verbs:
+ - watch
+ - list
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: calico-kube-controllers
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: calico-kube-controllers
+subjects:
+- kind: ServiceAccount
+ name: calico-kube-controllers
+ namespace: kube-system
+
+---
+
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: calico-node
+rules:
+ - apiGroups: [""]
+ resources:
+ - pods
+ - nodes
+ - namespaces
+ verbs:
+ - get
+ - apiGroups: [""]
+ resources:
+ - nodes/status
+ verbs:
+ - patch
+
+---
+
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ name: calico-node
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: calico-node
+subjects:
+- kind: ServiceAccount
+ name: calico-node
+ namespace: kube-system
diff --git a/roles/calico/templates/calico-v3.4.yaml.j2 b/roles/calico/templates/calico-v3.4.yaml.j2
new file mode 100644
index 0000000..17cb022
--- /dev/null
+++ b/roles/calico/templates/calico-v3.4.yaml.j2
@@ -0,0 +1,495 @@
+# Calico Version {{ calico_ver }}
+# https://docs.projectcalico.org/{{ calico_ver_main }}/releases#{{ calico_ver }}
+# This manifest includes the following component versions:
+# calico/node:{{ calico_ver }}
+# calico/cni:{{ calico_ver }}
+# calico/kube-controllers:{{ calico_ver }}
+
+# This ConfigMap is used to configure a self-hosted Calico installation.
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: calico-config
+ namespace: kube-system
+data:
+ # Configure this with the location of your etcd cluster.
+ etcd_endpoints: "{{ ETCD_ENDPOINTS }}"
+
+ # If you're using TLS enabled etcd uncomment the following.
+ # You must also populate the Secret below with these files.
+ etcd_ca: "/calico-secrets/etcd-ca"
+ etcd_cert: "/calico-secrets/etcd-cert"
+ etcd_key: "/calico-secrets/etcd-key"
+ # Configure the Calico backend to use.
+ calico_backend: "bird"
+
+ # Configure the MTU to use
+ veth_mtu: "1440"
+
+ # The CNI network configuration to install on each node.
+ cni_network_config: |-
+ {
+ "name": "k8s-pod-network",
+ "cniVersion": "0.3.0",
+ "plugins": [
+ {
+ "type": "calico",
+ "log_level": "warning",
+ "etcd_endpoints": "{{ ETCD_ENDPOINTS }}",
+ "etcd_key_file": "/etc/calico/ssl/calico-key.pem",
+ "etcd_cert_file": "/etc/calico/ssl/calico.pem",
+ "etcd_ca_cert_file": "/etc/kubernetes/ssl/ca.pem",
+ "mtu": 1500,
+ "ipam": {
+ "type": "calico-ipam"
+ },
+ "policy": {
+ "type": "k8s"
+ },
+ "kubernetes": {
+ "kubeconfig": "/root/.kube/config"
+ }
+ },
+ {
+ "type": "portmap",
+ "snat": true,
+ "capabilities": {"portMappings": true}
+ }
+ ]
+ }
+
+---
+
+# We use cmd-line-way( kubectl create) to create secrets 'calico-etcd-secrets',
+# refer to 'roles/calico/tasks/main.yml' for details.
+
+---
+
+# This manifest installs the calico/node container, as well
+# as the Calico CNI plugins and network config on
+# each master and worker node in a Kubernetes cluster.
+kind: DaemonSet
+apiVersion: extensions/v1beta1
+metadata:
+ name: calico-node
+ namespace: kube-system
+ labels:
+ k8s-app: calico-node
+spec:
+ selector:
+ matchLabels:
+ k8s-app: calico-node
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 1
+ template:
+ metadata:
+ labels:
+ k8s-app: calico-node
+ annotations:
+ # This, along with the CriticalAddonsOnly toleration below,
+ # marks the pod as a critical add-on, ensuring it gets
+ # priority scheduling and that its resources are reserved
+ # if it ever gets evicted.
+ scheduler.alpha.kubernetes.io/critical-pod: ''
+ spec:
+ nodeSelector:
+ beta.kubernetes.io/os: linux
+ hostNetwork: true
+ tolerations:
+ # Make sure calico-node gets scheduled on all nodes.
+ - effect: NoSchedule
+ operator: Exists
+ # Mark the pod as a critical add-on for rescheduling.
+ - key: CriticalAddonsOnly
+ operator: Exists
+ - effect: NoExecute
+ operator: Exists
+ serviceAccountName: calico-node
+ # Minimize downtime during a rolling upgrade or deletion; tell Kubernetes to do a "force
+ # deletion": https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods.
+ terminationGracePeriodSeconds: 0
+ initContainers:
+ # This container installs the Calico CNI binaries
+ # and CNI network config file on each node.
+ - name: install-cni
+ image: calico/cni:{{ calico_ver }}
+ command: ["/install-cni.sh"]
+ env:
+ # Name of the CNI config file to create.
+ - name: CNI_CONF_NAME
+ value: "10-calico.conflist"
+ # The CNI network config to install on each node.
+ - name: CNI_NETWORK_CONFIG
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: cni_network_config
+ # The location of the Calico etcd cluster.
+ - name: ETCD_ENDPOINTS
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_endpoints
+ # CNI MTU Config variable
+ - name: CNI_MTU
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: veth_mtu
+ # Prevents the container from sleeping forever.
+ - name: SLEEP
+ value: "false"
+ volumeMounts:
+ - mountPath: /host/opt/cni/bin
+ name: cni-bin-dir
+ - mountPath: /host/etc/cni/net.d
+ name: cni-net-dir
+ - mountPath: /calico-secrets
+ name: etcd-certs
+ containers:
+ # Runs calico/node container on each Kubernetes node. This
+ # container programs network policy and routes on each
+ # host.
+ - name: calico-node
+ image: calico/node:{{ calico_ver }}
+ env:
+ # The location of the Calico etcd cluster.
+ - name: ETCD_ENDPOINTS
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_endpoints
+ # Location of the CA certificate for etcd.
+ - name: ETCD_CA_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_ca
+ # Location of the client key for etcd.
+ - name: ETCD_KEY_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_key
+ # Location of the client certificate for etcd.
+ - name: ETCD_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_cert
+ # Set noderef for node controller.
+ - name: CALICO_K8S_NODE_REF
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.nodeName
+ # Choose the backend to use.
+ - name: CALICO_NETWORKING_BACKEND
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: calico_backend
+ # Cluster type to identify the deployment type
+ - name: CLUSTER_TYPE
+ value: "k8s,bgp"
+ # Auto-detect the BGP IP address.
+ - name: IP
+ value: "autodetect"
+ - name: IP_AUTODETECTION_METHOD
+ value: "{{ IP_AUTODETECTION_METHOD }}"
+ # Enable IPIP
+ - name: CALICO_IPV4POOL_IPIP
+ value: "{{ CALICO_IPV4POOL_IPIP }}"
+ # Set MTU for tunnel device used if ipip is enabled
+ - name: FELIX_IPINIPMTU
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: veth_mtu
+ # The default IPv4 pool to create on startup if none exists. Pod IPs will be
+ # chosen from this range. Changing this value after installation will have
+ # no effect. This should fall within `--cluster-cidr`.
+ - name: CALICO_IPV4POOL_CIDR
+ value: "{{ CLUSTER_CIDR }}"
+ # Disable file logging so `kubectl logs` works.
+ - name: CALICO_DISABLE_FILE_LOGGING
+ value: "true"
+ # Set Felix endpoint to host default action to ACCEPT.
+ - name: FELIX_DEFAULTENDPOINTTOHOSTACTION
+ value: "ACCEPT"
+ # Disable IPv6 on Kubernetes.
+ - name: FELIX_IPV6SUPPORT
+ value: "false"
+ # Set Felix logging
+ - name: FELIX_LOGSEVERITYSCREEN
+ value: "{{ FELIX_LOG_LVL }}"
+ - name: FELIX_HEALTHENABLED
+ value: "true"
+ # Set Kubernetes NodePorts: If services do use NodePorts outside Calico’s expected range,
+ # Calico will treat traffic to those ports as host traffic instead of pod traffic.
+ - name: FELIX_KUBENODEPORTRANGES
+ value: "{{ NODE_PORT_RANGE.split('-')[0] }}:{{ NODE_PORT_RANGE.split('-')[1] }}"
+ - name: FELIX_PROMETHEUSMETRICSENABLED
+ value: "true"
+ securityContext:
+ privileged: true
+ resources:
+ requests:
+ cpu: 250m
+ livenessProbe:
+ httpGet:
+ path: /liveness
+ port: 9099
+ host: localhost
+ periodSeconds: 10
+ initialDelaySeconds: 10
+ failureThreshold: 6
+ readinessProbe:
+ exec:
+ command:
+ - /bin/calico-node
+ - -bird-ready
+ - -felix-ready
+ periodSeconds: 10
+ volumeMounts:
+ - mountPath: /lib/modules
+ name: lib-modules
+ readOnly: true
+ - mountPath: /run/xtables.lock
+ name: xtables-lock
+ readOnly: false
+ - mountPath: /var/run/calico
+ name: var-run-calico
+ readOnly: false
+ - mountPath: /var/lib/calico
+ name: var-lib-calico
+ readOnly: false
+ - mountPath: /calico-secrets
+ name: etcd-certs
+ volumes:
+ # Used by calico/node.
+ - name: lib-modules
+ hostPath:
+ path: /lib/modules
+ - name: var-run-calico
+ hostPath:
+ path: /var/run/calico
+ - name: var-lib-calico
+ hostPath:
+ path: /var/lib/calico
+ - name: xtables-lock
+ hostPath:
+ path: /run/xtables.lock
+ type: FileOrCreate
+ # Used to install CNI.
+ - name: cni-bin-dir
+ hostPath:
+ path: {{ bin_dir }}
+ - name: cni-net-dir
+ hostPath:
+ path: /etc/cni/net.d
+ # Mount in the etcd TLS secrets with mode 400.
+ # See https://kubernetes.io/docs/concepts/configuration/secret/
+ - name: etcd-certs
+ secret:
+ secretName: calico-etcd-secrets
+ defaultMode: 0400
+---
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: calico-node
+ namespace: kube-system
+
+---
+
+# This manifest deploys the Calico Kubernetes controllers.
+# See https://github.com/projectcalico/kube-controllers
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: calico-kube-controllers
+ namespace: kube-system
+ labels:
+ k8s-app: calico-kube-controllers
+ annotations:
+ scheduler.alpha.kubernetes.io/critical-pod: ''
+spec:
+ # The controllers can only have a single active instance.
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ k8s-app: calico-kube-controllers
+ template:
+ metadata:
+ name: calico-kube-controllers
+ namespace: kube-system
+ labels:
+ k8s-app: calico-kube-controllers
+ spec:
+ nodeSelector:
+ beta.kubernetes.io/os: linux
+ # The controllers must run in the host network namespace so that
+ # it isn't governed by policy that would prevent it from working.
+ hostNetwork: true
+ tolerations:
+ # Mark the pod as a critical add-on for rescheduling.
+ - key: CriticalAddonsOnly
+ operator: Exists
+ - key: node-role.kubernetes.io/master
+ effect: NoSchedule
+ serviceAccountName: calico-kube-controllers
+ containers:
+ - name: calico-kube-controllers
+ image: calico/kube-controllers:{{ calico_ver }}
+ env:
+ # The location of the Calico etcd cluster.
+ - name: ETCD_ENDPOINTS
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_endpoints
+ # Location of the CA certificate for etcd.
+ - name: ETCD_CA_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_ca
+ # Location of the client key for etcd.
+ - name: ETCD_KEY_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_key
+ # Location of the client certificate for etcd.
+ - name: ETCD_CERT_FILE
+ valueFrom:
+ configMapKeyRef:
+ name: calico-config
+ key: etcd_cert
+ # Choose which controllers to run.
+ - name: ENABLED_CONTROLLERS
+ value: policy,namespace,serviceaccount,workloadendpoint,node
+{% if NODE_WITH_MULTIPLE_NETWORKS == 'true' %}
+ # if hosts have multiple net interfaces, set following two ENVs
+ - name: KUBERNETES_SERVICE_HOST
+ value: "{{ MASTER_IP }}"
+ - name: KUBERNETES_SERVICE_PORT
+ value: "{{ KUBE_APISERVER.split(':')[2] }}"
+{% endif %}
+ volumeMounts:
+ # Mount in the etcd TLS secrets.
+ - mountPath: /calico-secrets
+ name: etcd-certs
+ readinessProbe:
+ exec:
+ command:
+ - /usr/bin/check-status
+ - -r
+ volumes:
+ # Mount in the etcd TLS secrets with mode 400.
+ # See https://kubernetes.io/docs/concepts/configuration/secret/
+ - name: etcd-certs
+ secret:
+ secretName: calico-etcd-secrets
+ defaultMode: 0400
+
+---
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: calico-kube-controllers
+ namespace: kube-system
+---
+
+# Include a clusterrole for the kube-controllers component,
+# and bind it to the calico-kube-controllers serviceaccount.
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: calico-kube-controllers
+rules:
+ # Pods are monitored for changing labels.
+ # The node controller monitors Kubernetes nodes.
+ # Namespace and serviceaccount labels are used for policy.
+ - apiGroups:
+ - ""
+ resources:
+ - pods
+ - nodes
+ - namespaces
+ - serviceaccounts
+ verbs:
+ - watch
+ - list
+ # Watch for changes to Kubernetes NetworkPolicies.
+ - apiGroups:
+ - networking.k8s.io
+ resources:
+ - networkpolicies
+ verbs:
+ - watch
+ - list
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: calico-kube-controllers
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: calico-kube-controllers
+subjects:
+- kind: ServiceAccount
+ name: calico-kube-controllers
+ namespace: kube-system
+---
+# Include a clusterrole for the calico-node DaemonSet,
+# and bind it to the calico-node serviceaccount.
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: calico-node
+rules:
+ # The CNI plugin needs to get pods, nodes, and namespaces.
+ - apiGroups: [""]
+ resources:
+ - pods
+ - nodes
+ - namespaces
+ verbs:
+ - get
+ - apiGroups: [""]
+ resources:
+ - endpoints
+ - services
+ verbs:
+ # Used to discover service IPs for advertisement.
+ - watch
+ - list
+ - apiGroups: [""]
+ resources:
+ - nodes/status
+ verbs:
+ # Needed for clearing NodeNetworkUnavailable flag.
+ - patch
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ name: calico-node
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: calico-node
+subjects:
+- kind: ServiceAccount
+ name: calico-node
+ namespace: kube-system
+---
+
diff --git a/roles/calico/templates/calicoctl.cfg.j2 b/roles/calico/templates/calicoctl.cfg.j2
new file mode 100644
index 0000000..13b8a2f
--- /dev/null
+++ b/roles/calico/templates/calicoctl.cfg.j2
@@ -0,0 +1,9 @@
+apiVersion: projectcalico.org/v3
+kind: CalicoAPIConfig
+metadata:
+spec:
+ datastoreType: "etcdv3"
+ etcdEndpoints: {{ ETCD_ENDPOINTS }}
+ etcdKeyFile: /etc/calico/ssl/calico-key.pem
+ etcdCertFile: /etc/calico/ssl/calico.pem
+ etcdCACertFile: {{ ca_dir }}/ca.pem
diff --git a/roles/chrony/chrony.yml b/roles/chrony/chrony.yml
new file mode 100644
index 0000000..ea2d3c1
--- /dev/null
+++ b/roles/chrony/chrony.yml
@@ -0,0 +1,3 @@
+- hosts: all
+ roles:
+ - { role: chrony, when: "hostvars[groups.deploy[0]]['NTP_ENABLED'] == 'yes'" }
diff --git a/roles/chrony/defaults/main.yml b/roles/chrony/defaults/main.yml
new file mode 100644
index 0000000..8a6089e
--- /dev/null
+++ b/roles/chrony/defaults/main.yml
@@ -0,0 +1,7 @@
+# 设置时间源服务器
+ntp_server: "ntp1.aliyun.com"
+
+# 设置允许内部时间同步的网络段,比如"10.0.0.0/8",默认全部允许
+local_network: "0.0.0.0/0"
+
+
diff --git a/roles/chrony/tasks/main.yml b/roles/chrony/tasks/main.yml
new file mode 100644
index 0000000..0d9aa0d
--- /dev/null
+++ b/roles/chrony/tasks/main.yml
@@ -0,0 +1,46 @@
+- name: apt更新缓存刷新
+ apt: update_cache=yes cache_valid_time=72000
+ when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version|int >= 16
+
+- name: 确保已卸载ntp
+ package: name=ntp state=absent
+
+- name: 安装 chrony
+ package: name=chrony state=latest
+
+- block:
+ - name: 配置 chrony server
+ template: src=server-centos.conf.j2 dest=/etc/chrony.conf
+ when: (ansible_distribution == "CentOS" or ansible_distribution == "RedHat") and ansible_distribution_major_version == "7"
+
+ - name: 配置 chrony server
+ template: src=server-ubuntu.conf.j2 dest=/etc/chrony/chrony.conf
+ when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version|int >= 16
+
+ - name: 启动 chrony server
+ service: name=chronyd state=restarted enabled=yes
+ when: (ansible_distribution == "CentOS" or ansible_distribution == "RedHat") and ansible_distribution_major_version == "7"
+
+ - name: 启动 chrony server
+ service: name=chrony state=restarted enabled=yes
+ when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version|int >= 16
+ when: 'inventory_hostname == groups.deploy[0]'
+
+- block:
+ - name: 配置 chrony client
+ template: src=client-centos.conf.j2 dest=/etc/chrony.conf
+ when: (ansible_distribution == "CentOS" or ansible_distribution == "RedHat") and ansible_distribution_major_version == "7"
+
+ - name: 配置 chrony client
+ template: src=client-ubuntu.conf.j2 dest=/etc/chrony/chrony.conf
+ when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version|int >= 16
+
+ - name: 启动 chrony client
+ service: name=chronyd state=restarted enabled=yes
+ when: (ansible_distribution == "CentOS" or ansible_distribution == "RedHat") and ansible_distribution_major_version == "7"
+
+ - name: 启动 chrony client
+ service: name=chrony state=restarted enabled=yes
+ when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version|int >= 16
+ when: 'inventory_hostname != groups.deploy[0]'
+
diff --git a/roles/chrony/templates/client-centos.conf.j2 b/roles/chrony/templates/client-centos.conf.j2
new file mode 100644
index 0000000..2c8f1f0
--- /dev/null
+++ b/roles/chrony/templates/client-centos.conf.j2
@@ -0,0 +1,44 @@
+# Use public servers from the pool.ntp.org project.
+server {{ groups.deploy[0] }} iburst
+
+# Ignor source level
+stratumweight 0
+
+# Record the rate at which the system clock gains/losses time.
+driftfile /var/lib/chrony/drift
+
+# Allow the system clock to be stepped in the first three updates
+# if its offset is larger than 1 second.
+makestep 1.0 3
+
+# Enable kernel synchronization of the real-time clock (RTC).
+rtcsync
+
+# Enable hardware timestamping on all interfaces that support it.
+#hwtimestamp *
+
+# Increase the minimum number of selectable sources required to adjust
+# the system clock.
+#minsources 2
+
+# Allow NTP client access from local network.
+allow {{ local_network }}
+
+#
+bindcmdaddress 127.0.0.1
+bindcmdaddress ::1
+
+# Serve time even if not synchronized to a time source.
+#local stratum 10
+
+# Specify file containing keys for NTP authentication.
+keyfile /etc/chrony.keys
+
+# Specify directory for log files.
+logdir /var/log/chrony
+
+# Select which information is logged.
+#log measurements statistics tracking
+
+#
+logchange 1
diff --git a/roles/chrony/templates/client-ubuntu.conf.j2 b/roles/chrony/templates/client-ubuntu.conf.j2
new file mode 100644
index 0000000..142a746
--- /dev/null
+++ b/roles/chrony/templates/client-ubuntu.conf.j2
@@ -0,0 +1,56 @@
+# Use public servers from the pool.ntp.org project.
+pool {{ groups.deploy[0] }} iburst
+
+# Look here for the admin password needed for chronyc. The initial
+# password is generated by a random process at install time. You may
+# change it if you wish.
+keyfile /etc/chrony/chrony.keys
+
+# This directive sets the key ID used for authenticating user commands via the
+# 'chronyc' program at run time.
+commandkey 1
+
+# I moved the driftfile to /var/lib/chrony to comply with the Debian
+# filesystem standard.
+driftfile /var/lib/chrony/chrony.drift
+
+# Comment this line out to turn off logging.
+#log tracking measurements statistics
+logdir /var/log/chrony
+
+# Stop bad estimates upsetting machine clock.
+maxupdateskew 100.0
+
+# Dump measurements when daemon exits.
+#dumponexit
+
+# Specify directory for dumping measurements.
+dumpdir /var/lib/chrony
+
+# This directive lets 'chronyd' to serve time even if unsynchronised to any
+# NTP server.
+#local stratum 10
+
+# This directive designates subnets (or nodes) from which NTP clients are allowed
+# to access to 'chronyd'.
+allow {{ local_network }}
+
+# This directive forces `chronyd' to send a message to syslog if it
+# makes a system clock adjustment larger than a threshold value in seconds.
+logchange 1
+
+# This directive defines an email address to which mail should be sent
+# if chronyd applies a correction exceeding a particular threshold to the
+# system clock.
+
+# mailonchange root@localhost 0.5
+
+# This directive tells 'chronyd' to parse the 'adjtime' file to find out if the
+# real-time clock keeps local time or UTC. It overrides the 'rtconutc' directive.
+
+hwclockfile /etc/adjtime
+
+# This directive enables kernel synchronisation (every 11 minutes) of the
+# real-time clock. Note that it can’t be used along with the 'rtcfile' directive.
+
+rtcsync
diff --git a/roles/chrony/templates/server-centos.conf.j2 b/roles/chrony/templates/server-centos.conf.j2
new file mode 100644
index 0000000..261edc3
--- /dev/null
+++ b/roles/chrony/templates/server-centos.conf.j2
@@ -0,0 +1,49 @@
+# Use public servers from the pool.ntp.org project.
+server {{ ntp_server }} iburst
+server 0.centos.pool.ntp.org iburst
+server 1.centos.pool.ntp.org iburst
+server 2.centos.pool.ntp.org iburst
+server 3.centos.pool.ntp.org iburst
+
+# Ignor source level
+stratumweight 0
+
+# Record the rate at which the system clock gains/losses time.
+driftfile /var/lib/chrony/drift
+
+# Allow the system clock to be stepped in the first three updates
+# if its offset is larger than 1 second.
+makestep 1.0 3
+
+# Enable kernel synchronization of the real-time clock (RTC).
+rtcsync
+
+# Enable hardware timestamping on all interfaces that support it.
+#hwtimestamp *
+
+# Increase the minimum number of selectable sources required to adjust
+# the system clock.
+#minsources 2
+
+# Allow NTP client access from local network.
+allow {{ local_network }}
+
+#
+bindcmdaddress 127.0.0.1
+bindcmdaddress ::1
+
+# Serve time even if not synchronized to a time source.
+local stratum 10
+
+# Specify file containing keys for NTP authentication.
+keyfile /etc/chrony.keys
+
+# Specify directory for log files.
+logdir /var/log/chrony
+
+# Select which information is logged.
+#log measurements statistics tracking
+
+#
+noclientlog
+logchange 1
diff --git a/roles/chrony/templates/server-ubuntu.conf.j2 b/roles/chrony/templates/server-ubuntu.conf.j2
new file mode 100644
index 0000000..41fd4da
--- /dev/null
+++ b/roles/chrony/templates/server-ubuntu.conf.j2
@@ -0,0 +1,57 @@
+# Use public servers from the pool.ntp.org project.
+pool {{ ntp_server }} iburst
+pool 2.debian.pool.ntp.org iburst
+
+# Look here for the admin password needed for chronyc. The initial
+# password is generated by a random process at install time. You may
+# change it if you wish.
+keyfile /etc/chrony/chrony.keys
+
+# This directive sets the key ID used for authenticating user commands via the
+# 'chronyc' program at run time.
+commandkey 1
+
+# I moved the driftfile to /var/lib/chrony to comply with the Debian
+# filesystem standard.
+driftfile /var/lib/chrony/chrony.drift
+
+# Comment this line out to turn off logging.
+#log tracking measurements statistics
+logdir /var/log/chrony
+
+# Stop bad estimates upsetting machine clock.
+maxupdateskew 100.0
+
+# Dump measurements when daemon exits.
+#dumponexit
+
+# Specify directory for dumping measurements.
+dumpdir /var/lib/chrony
+
+# This directive lets 'chronyd' to serve time even if unsynchronised to any
+# NTP server.
+local stratum 10
+
+# This directive designates subnets (or nodes) from which NTP clients are allowed
+# to access to 'chronyd'.
+allow {{ local_network }}
+
+# This directive forces `chronyd' to send a message to syslog if it
+# makes a system clock adjustment larger than a threshold value in seconds.
+logchange 1
+
+# This directive defines an email address to which mail should be sent
+# if chronyd applies a correction exceeding a particular threshold to the
+# system clock.
+
+# mailonchange root@localhost 0.5
+
+# This directive tells 'chronyd' to parse the 'adjtime' file to find out if the
+# real-time clock keeps local time or UTC. It overrides the 'rtconutc' directive.
+
+hwclockfile /etc/adjtime
+
+# This directive enables kernel synchronisation (every 11 minutes) of the
+# real-time clock. Note that it can’t be used along with the 'rtcfile' directive.
+
+rtcsync
diff --git a/roles/cilium/cilium.yml b/roles/cilium/cilium.yml
new file mode 100644
index 0000000..5e84dbb
--- /dev/null
+++ b/roles/cilium/cilium.yml
@@ -0,0 +1,5 @@
+- hosts:
+ - kube-master
+ - kube-node
+ roles:
+ - cilium
diff --git a/roles/cilium/defaults/main.yml b/roles/cilium/defaults/main.yml
new file mode 100644
index 0000000..4f38ba6
--- /dev/null
+++ b/roles/cilium/defaults/main.yml
@@ -0,0 +1,14 @@
+# 部分cilium相关配置, Note: cilium 需要Linux kernel >= 4.9.17
+
+# 如果 node 节点有多块网卡,请设置 true
+# 如果发现‘dial tcp 10.68.0.1:443: i/o timeout’的错误,请设置 true
+NODE_WITH_MULTIPLE_NETWORKS: "true"
+
+# 镜像版本
+cilium_ver: "v1.4.1"
+
+# 离线镜像tar包
+cilium_offline: "cilium_{{ cilium_ver }}.tar"
+
+# CILIUM_ETCD_OPERATOR 创建的 etcd 集群节点数 1,3,5,7...
+ETCD_CLUSTER_SIZE: 1
diff --git a/roles/cilium/files/star_war_example/http-sw-app.yaml b/roles/cilium/files/star_war_example/http-sw-app.yaml
new file mode 100644
index 0000000..006e61d
--- /dev/null
+++ b/roles/cilium/files/star_war_example/http-sw-app.yaml
@@ -0,0 +1,56 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: deathstar
+spec:
+ type: ClusterIP
+ ports:
+ - port: 80
+ selector:
+ org: empire
+ class: deathstar
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: deathstar
+spec:
+ replicas: 2
+ selector:
+ matchLabels:
+ org: empire
+ class: deathstar
+ template:
+ metadata:
+ labels:
+ org: empire
+ class: deathstar
+ spec:
+ containers:
+ - name: deathstar
+ image: docker.io/cilium/starwars
+---
+apiVersion: v1
+kind: Pod
+metadata:
+ name: tiefighter
+ labels:
+ org: empire
+ class: tiefighter
+spec:
+ containers:
+ - name: spaceship
+ image: docker.io/tgraf/netperf
+---
+apiVersion: v1
+kind: Pod
+metadata:
+ name: xwing
+ labels:
+ org: alliance
+ class: xwing
+spec:
+ containers:
+ - name: spaceship
+ image: docker.io/tgraf/netperf
diff --git a/roles/cilium/files/star_war_example/sw_l3_l4_l7_policy.yaml b/roles/cilium/files/star_war_example/sw_l3_l4_l7_policy.yaml
new file mode 100644
index 0000000..1a396f7
--- /dev/null
+++ b/roles/cilium/files/star_war_example/sw_l3_l4_l7_policy.yaml
@@ -0,0 +1,22 @@
+apiVersion: "cilium.io/v2"
+kind: CiliumNetworkPolicy
+description: "L7 policy to restrict access to specific HTTP call"
+metadata:
+ name: "rule1"
+spec:
+ endpointSelector:
+ matchLabels:
+ org: empire
+ class: deathstar
+ ingress:
+ - fromEndpoints:
+ - matchLabels:
+ org: empire
+ toPorts:
+ - ports:
+ - port: "80"
+ protocol: TCP
+ rules:
+ http:
+ - method: "POST"
+ path: "/v1/request-landing"
diff --git a/roles/cilium/files/star_war_example/sw_l3_l4_policy.yaml b/roles/cilium/files/star_war_example/sw_l3_l4_policy.yaml
new file mode 100644
index 0000000..9db99cc
--- /dev/null
+++ b/roles/cilium/files/star_war_example/sw_l3_l4_policy.yaml
@@ -0,0 +1,18 @@
+apiVersion: "cilium.io/v2"
+kind: CiliumNetworkPolicy
+description: "L3-L4 policy to restrict deathstar access to empire ships only"
+metadata:
+ name: "rule1"
+spec:
+ endpointSelector:
+ matchLabels:
+ org: empire
+ class: deathstar
+ ingress:
+ - fromEndpoints:
+ - matchLabels:
+ org: empire
+ toPorts:
+ - ports:
+ - port: "80"
+ protocol: TCP
diff --git a/roles/cilium/tasks/main.yml b/roles/cilium/tasks/main.yml
new file mode 100644
index 0000000..efb49a0
--- /dev/null
+++ b/roles/cilium/tasks/main.yml
@@ -0,0 +1,80 @@
+- name: 在deploy 节点创建cilium 相关目录
+ file: name=/opt/kube/kube-system/cilium state=directory
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+- name: 配置 cilium DaemonSet yaml文件
+ template: src=cilium.yaml.j2 dest=/opt/kube/kube-system/cilium/cilium.yaml
+ tags: reconf
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+- name: 转换内核版本为浮点数
+ set_fact:
+ KERNEL_VER: "{{ ansible_kernel.split('-')[0].split('.')[0]|int + ansible_kernel.split('-')[0].split('.')[1]|int/100 }}"
+
+- name: 检查内核版本>4.9
+ fail: msg="kernel {{ ansible_kernel }} is too old for cilium installing"
+ when: "KERNEL_VER <= 4.09"
+
+- name: 检查是否已下载离线cilium镜像
+ command: "ls {{ base_dir }}/down"
+ register: download_info
+ connection: local
+ run_once: true
+
+- name: node 节点创建cilium 相关目录
+ file: name={{ item }} state=directory
+ with_items:
+ - /etc/cni/net.d
+ - /var/run/cilium
+ - /opt/kube/images
+
+- name: Optional-Mount BPF FS
+ mount:
+ fstype: "bpf"
+ src: "bpffs"
+ path: "/sys/fs/bpf"
+ state: "mounted"
+
+# 【可选】推送离线docker 镜像,可以忽略执行错误
+- name: 尝试推送离线docker 镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ item }} dest=/opt/kube/images/{{ item }}
+ when: 'item in download_info.stdout'
+ with_items:
+ - "pause_3.1.tar"
+ - "{{ cilium_offline }}"
+ ignore_errors: true
+
+- name: 获取cilium离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+# 如果目录下有离线镜像,就把它导入到node节点上
+- name: 导入 cilium的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ item }}"
+ when: 'item in image_info.stdout'
+ with_items:
+ - "pause_3.1.tar"
+ - "{{ cilium_offline }}"
+ ignore_errors: true
+
+# 只需单节点执行一次
+- name: 运行 cilium网络
+ shell: "{{ bin_dir }}/kubectl apply -f /opt/kube/kube-system/cilium/ && sleep 5"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+# 删除原有cni配置
+- name: 删除默认cni配置
+ file: path=/etc/cni/net.d/10-default.conf state=absent
+
+# 等待网络插件部署成功,视下载镜像速度而定
+- name: 轮询等待cilium-node 运行,视下载镜像速度而定
+ shell: "{{ bin_dir }}/kubectl get pod -n kube-system -o wide|grep 'cilium'|grep ' {{ inventory_hostname }} '|awk '{print $3}'"
+ register: pod_status
+ until: pod_status.stdout == "Running"
+ delegate_to: "{{ groups.deploy[0] }}"
+ retries: 15
+ delay: 8
+ ignore_errors: true
diff --git a/roles/cilium/templates/cilium-csr.json.j2 b/roles/cilium/templates/cilium-csr.json.j2
new file mode 100644
index 0000000..28c9484
--- /dev/null
+++ b/roles/cilium/templates/cilium-csr.json.j2
@@ -0,0 +1,17 @@
+{
+ "CN": "cilium",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/cilium/templates/cilium.yaml.j2 b/roles/cilium/templates/cilium.yaml.j2
new file mode 100644
index 0000000..30cbd82
--- /dev/null
+++ b/roles/cilium/templates/cilium.yaml.j2
@@ -0,0 +1,992 @@
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: cilium-config
+ namespace: kube-system
+data:
+ # This etcd-config contains the etcd endpoints of your cluster. If you use
+ # TLS please make sure you follow the tutorial in https://cilium.link/etcd-config
+ etcd-config: |-
+ ---
+ endpoints:
+ - https://cilium-etcd-client.kube-system.svc:2379
+ #
+ # In case you want to use TLS in etcd, uncomment the 'ca-file' line
+ # and create a kubernetes secret by following the tutorial in
+ # https://cilium.link/etcd-config
+ ca-file: '/var/lib/etcd-secrets/etcd-client-ca.crt'
+ #
+ # In case you want client to server authentication, uncomment the following
+ # lines and create a kubernetes secret by following the tutorial in
+ # https://cilium.link/etcd-config
+ key-file: '/var/lib/etcd-secrets/etcd-client.key'
+ cert-file: '/var/lib/etcd-secrets/etcd-client.crt'
+
+ # If you want to run cilium in debug mode change this value to true
+ debug: "false"
+
+ # Enable IPv4 addressing. If enabled, all endpoints are allocated an IPv4
+ # address.
+ enable-ipv4: "true"
+
+ # Enable IPv6 addressing. If enabled, all endpoints are allocated an IPv6
+ # address.
+ enable-ipv6: "false"
+
+ # If a serious issue occurs during Cilium startup, this
+ # invasive option may be set to true to remove all persistent
+ # state. Endpoints will not be restored using knowledge from a
+ # prior Cilium run, so they may receive new IP addresses upon
+ # restart. This also triggers clean-cilium-bpf-state.
+ clean-cilium-state: "false"
+ # If you want to clean cilium BPF state, set this to true;
+ # Removes all BPF maps from the filesystem. Upon restart,
+ # endpoints are restored with the same IP addresses, however
+ # any ongoing connections may be disrupted briefly.
+ # Loadbalancing decisions will be reset, so any ongoing
+ # connections via a service may be loadbalanced to a different
+ # backend after restart.
+ clean-cilium-bpf-state: "false"
+
+ # In Cilium 1.0, all traffic from the host, including from local processes
+ # and traffic that is masqueraded from the outside world to the host IP,
+ # would be classified as from the host entity (reserved:host label).
+ # Furthermore, to allow Kubernetes agents to perform health checks over IP
+ # into the endpoints, the host is allowed by default. This means that all
+ # traffic from the outside world is also allowed by default, regardless of
+ # security policy.
+ #
+ # This option was introduced in Cilium 1.1 to disable this behaviour. It must
+ # be explicitly set to "false" to take effect on Cilium 1.3 or earlier.
+ # Cilium 1.4 sets this to "false" by default if it is not specified in the
+ # ConfigMap.
+ #
+ # This option has been deprecated, it will be removed in Cilium 1.5 or later.
+ #
+ # For more information, see https://cilium.link/host-vs-world
+ #legacy-host-allows-world: "false"
+
+ # If you want cilium monitor to aggregate tracing for packets, set this level
+ # to "low", "medium", or "maximum". The higher the level, the less packets
+ # that will be seen in monitor output.
+ monitor-aggregation-level: "none"
+
+ # ct-global-max-entries-* specifies the maximum number of connections
+ # supported across all endpoints, split by protocol: tcp or other. One pair
+ # of maps uses these values for IPv4 connections, and another pair of maps
+ # use these values for IPv6 connections.
+ #
+ # If these values are modified, then during the next Cilium startup the
+ # tracking of ongoing connections may be disrupted. This may lead to brief
+ # policy drops or a change in loadbalancing decisions for a connection.
+ #
+ # For users upgrading from Cilium 1.2 or earlier, to minimize disruption
+ # during the upgrade process, comment out these options.
+ ct-global-max-entries-tcp: "524288"
+ ct-global-max-entries-other: "262144"
+
+ # Pre-allocation of map entries allows per-packet latency to be reduced, at
+ # the expense of up-front memory allocation for the entries in the maps. The
+ # default value below will minimize memory usage in the default installation;
+ # users who are sensitive to latency may consider setting this to "true".
+ #
+ # This option was introduced in Cilium 1.4. Cilium 1.3 and earlier ignore
+ # this option and behave as though it is set to "true".
+ #
+ # If this value is modified, then during the next Cilium startup the restore
+ # of existing endpoints and tracking of ongoing connections may be disrupted.
+ # This may lead to policy drops or a change in loadbalancing decisions for a
+ # connection for some time. Endpoints may need to be recreated to restore
+ # connectivity.
+ #
+ # If this option is set to "false" during an upgrade from 1.3 or earlier to
+ # 1.4 or later, then it may cause one-time disruptions during the upgrade.
+ preallocate-bpf-maps: "false"
+
+ # Regular expression matching compatible Istio sidecar istio-proxy
+ # container image names
+ sidecar-istio-proxy-image: "cilium/istio_proxy"
+
+ # Encapsulation mode for communication between nodes
+ # Possible values:
+ # - disabled
+ # - vxlan (default)
+ # - geneve
+ tunnel: "vxlan"
+
+ # Name of the cluster. Only relevant when building a mesh of clusters.
+ cluster-name: default
+
+ # Unique ID of the cluster. Must be unique across all conneted clusters and
+ # in the range of 1 and 255. Only relevant when building a mesh of clusters.
+ #cluster-id: 1
+
+ # Interface to be used when running Cilium on top of a CNI plugin.
+ # For flannel, use "cni0"
+ flannel-master-device: ""
+ # When running Cilium with policy enforcement enabled on top of a CNI plugin
+ # the BPF programs will be installed on the network interface specified in
+ # 'flannel-master-device' and on all network interfaces belonging to
+ # a container. When the Cilium DaemonSet is removed, the BPF programs will
+ # be kept in the interfaces unless this option is set to "true".
+ flannel-uninstall-on-exit: "true"
+ # Installs a BPF program to allow for policy enforcement in already running
+ # containers managed by Flannel.
+ # NOTE: This requires Cilium DaemonSet to be running in the hostPID.
+ # To run in this mode in Kubernetes change the value of the hostPID from
+ # false to true. Can be found under the path `spec.spec.hostPID`
+ flannel-manage-existing-containers: "false"
+
+ # DNS Polling periodically issues a DNS lookup for each `matchName` from
+ # cilium-agent. The result is used to regenerate endpoint policy.
+ # DNS lookups are repeated with an interval of 5 seconds, and are made for
+ # A(IPv4) and AAAA(IPv6) addresses. Should a lookup fail, the most recent IP
+ # data is used instead. An IP change will trigger a regeneration of the Cilium
+ # policy for each endpoint and increment the per cilium-agent policy
+ # repository revision.
+ #
+ # This option is disabled by default starting from version 1.4.x in favor
+ # of a more powerful DNS proxy-based implementation, see [0] for details.
+ # Enable this option if you want to use FQDN policies but do not want to use
+ # the DNS proxy.
+ #
+ # To ease upgrade, users may opt to set this option to "true".
+ # Otherwise please refer to the Upgrade Guide [1] which explains how to
+ # prepare policy rules for upgrade.
+ #
+ # [0] http://docs.cilium.io/en/stable/policy/language/#dns-based
+ # [1] http://docs.cilium.io/en/stable/install/upgrade/#changes-that-may-require-action
+ tofqdns-enable-poller: "false"
+---
+apiVersion: apps/v1beta2
+kind: DaemonSet
+metadata:
+ labels:
+ k8s-app: cilium
+ kubernetes.io/cluster-service: "true"
+ name: cilium
+ namespace: kube-system
+spec:
+ selector:
+ matchLabels:
+ k8s-app: cilium
+ kubernetes.io/cluster-service: "true"
+ template:
+ metadata:
+ annotations:
+ prometheus.io/port: "9090"
+ prometheus.io/scrape: "true"
+ # This annotation plus the CriticalAddonsOnly toleration makes
+ # cilium to be a critical pod in the cluster, which ensures cilium
+ # gets priority scheduling.
+ # https://kubernetes.io/docs/tasks/administer-cluster/guaranteed-scheduling-critical-addon-pods/
+ scheduler.alpha.kubernetes.io/critical-pod: ""
+ scheduler.alpha.kubernetes.io/tolerations: '[{"key":"dedicated","operator":"Equal","value":"master","effect":"NoSchedule"}]'
+ labels:
+ k8s-app: cilium
+ kubernetes.io/cluster-service: "true"
+ spec:
+ containers:
+ - args:
+ - --debug=$(CILIUM_DEBUG)
+ - --kvstore=etcd
+ - --kvstore-opt=etcd.config=/var/lib/etcd-config/etcd.config
+ command:
+ - cilium-agent
+ env:
+ - name: K8S_NODE_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: spec.nodeName
+ - name: CILIUM_K8S_NAMESPACE
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.namespace
+ - name: CILIUM_DEBUG
+ valueFrom:
+ configMapKeyRef:
+ key: debug
+ name: cilium-config
+ - name: CILIUM_ENABLE_IPV4
+ valueFrom:
+ configMapKeyRef:
+ key: enable-ipv4
+ name: cilium-config
+ optional: true
+ - name: CILIUM_ENABLE_IPV6
+ valueFrom:
+ configMapKeyRef:
+ key: enable-ipv6
+ name: cilium-config
+ optional: true
+ # Note: this variable is a no-op if not defined, and is used in the
+ # prometheus examples.
+ - name: CILIUM_PROMETHEUS_SERVE_ADDR
+ valueFrom:
+ configMapKeyRef:
+ key: prometheus-serve-addr
+ name: cilium-metrics-config
+ optional: true
+ - name: CILIUM_LEGACY_HOST_ALLOWS_WORLD
+ valueFrom:
+ configMapKeyRef:
+ key: legacy-host-allows-world
+ name: cilium-config
+ optional: true
+ - name: CILIUM_SIDECAR_ISTIO_PROXY_IMAGE
+ valueFrom:
+ configMapKeyRef:
+ key: sidecar-istio-proxy-image
+ name: cilium-config
+ optional: true
+ - name: CILIUM_TUNNEL
+ valueFrom:
+ configMapKeyRef:
+ key: tunnel
+ name: cilium-config
+ optional: true
+ - name: CILIUM_MONITOR_AGGREGATION_LEVEL
+ valueFrom:
+ configMapKeyRef:
+ key: monitor-aggregation-level
+ name: cilium-config
+ optional: true
+ - name: CILIUM_CLUSTERMESH_CONFIG
+ value: /var/lib/cilium/clustermesh/
+ - name: CILIUM_CLUSTER_NAME
+ valueFrom:
+ configMapKeyRef:
+ key: cluster-name
+ name: cilium-config
+ optional: true
+ - name: CILIUM_CLUSTER_ID
+ valueFrom:
+ configMapKeyRef:
+ key: cluster-id
+ name: cilium-config
+ optional: true
+ - name: CILIUM_GLOBAL_CT_MAX_TCP
+ valueFrom:
+ configMapKeyRef:
+ key: ct-global-max-entries-tcp
+ name: cilium-config
+ optional: true
+ - name: CILIUM_GLOBAL_CT_MAX_ANY
+ valueFrom:
+ configMapKeyRef:
+ key: ct-global-max-entries-other
+ name: cilium-config
+ optional: true
+ - name: CILIUM_PREALLOCATE_BPF_MAPS
+ valueFrom:
+ configMapKeyRef:
+ key: preallocate-bpf-maps
+ name: cilium-config
+ optional: true
+ - name: CILIUM_FLANNEL_MASTER_DEVICE
+ valueFrom:
+ configMapKeyRef:
+ key: flannel-master-device
+ name: cilium-config
+ optional: true
+ - name: CILIUM_FLANNEL_UNINSTALL_ON_EXIT
+ valueFrom:
+ configMapKeyRef:
+ key: flannel-uninstall-on-exit
+ name: cilium-config
+ optional: true
+ - name: CILIUM_FLANNEL_MANAGE_EXISTING_CONTAINERS
+ valueFrom:
+ configMapKeyRef:
+ key: flannel-manage-existing-containers
+ name: cilium-config
+ optional: true
+ - name: CILIUM_DATAPATH_MODE
+ valueFrom:
+ configMapKeyRef:
+ key: datapath-mode
+ name: cilium-config
+ optional: true
+ - name: CILIUM_IPVLAN_MASTER_DEVICE
+ valueFrom:
+ configMapKeyRef:
+ key: ipvlan-master-device
+ name: cilium-config
+ optional: true
+ - name: CILIUM_INSTALL_IPTABLES_RULES
+ valueFrom:
+ configMapKeyRef:
+ key: install-iptables-rules
+ name: cilium-config
+ optional: true
+ - name: CILIUM_MASQUERADE
+ valueFrom:
+ configMapKeyRef:
+ key: masquerade
+ name: cilium-config
+ optional: true
+ - name: CILIUM_AUTO_DIRECT_NODE_ROUTES
+ valueFrom:
+ configMapKeyRef:
+ key: auto-direct-node-routes
+ name: cilium-config
+ optional: true
+ - name: CILIUM_TOFQDNS_ENABLE_POLLER
+ valueFrom:
+ configMapKeyRef:
+ key: tofqdns-enable-poller
+ name: cilium-config
+ optional: true
+ - name: CILIUM_TOFQDNS_PRE_CACHE
+ valueFrom:
+ configMapKeyRef:
+ key: tofqdns-pre-cache
+ name: cilium-config
+ optional: true
+{% if NODE_WITH_MULTIPLE_NETWORKS == 'true' %}
+ # if hosts have multiple net interfaces, set following two ENVs
+ - name: KUBERNETES_SERVICE_HOST
+ value: "{{ MASTER_IP }}"
+ #value: "{{ KUBE_APISERVER.split(':')[1].lstrip('/') }}"
+ - name: KUBERNETES_SERVICE_PORT
+ value: "{{ KUBE_APISERVER.split(':')[2] }}"
+{% endif %}
+ image: docker.io/cilium/cilium:{{ cilium_ver }}
+ imagePullPolicy: IfNotPresent
+ lifecycle:
+ postStart:
+ exec:
+ command:
+ - /cni-install.sh
+ preStop:
+ exec:
+ command:
+ - /cni-uninstall.sh
+ livenessProbe:
+ exec:
+ command:
+ - cilium
+ - status
+ failureThreshold: 10
+ # The initial delay for the liveness probe is intentionally large to
+ # avoid an endless kill & restart cycle if in the event that the initial
+ # bootstrapping takes longer than expected.
+ initialDelaySeconds: 120
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 1
+ name: cilium-agent
+ ports:
+ - containerPort: 9090
+ hostPort: 9090
+ name: prometheus
+ protocol: TCP
+ readinessProbe:
+ exec:
+ command:
+ - cilium
+ - status
+ failureThreshold: 3
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ successThreshold: 1
+ timeoutSeconds: 1
+ securityContext:
+ capabilities:
+ add:
+ - NET_ADMIN
+ privileged: true
+ volumeMounts:
+ - mountPath: /sys/fs/bpf
+ name: bpf-maps
+ - mountPath: /var/run/cilium
+ name: cilium-run
+ - mountPath: /host/opt/cni/bin
+ name: cni-path
+ - mountPath: /host/etc/cni/net.d
+ name: etc-cni-netd
+ - mountPath: /var/run/docker.sock
+ name: docker-socket
+ readOnly: true
+ - mountPath: /var/lib/etcd-config
+ name: etcd-config-path
+ readOnly: true
+ - mountPath: /var/lib/etcd-secrets
+ name: etcd-secrets
+ readOnly: true
+ - mountPath: /var/lib/cilium/clustermesh
+ name: clustermesh-secrets
+ readOnly: true
+ dnsPolicy: ClusterFirstWithHostNet
+ hostNetwork: true
+ hostPID: false
+ initContainers:
+ - command:
+ - /init-container.sh
+ env:
+ - name: CLEAN_CILIUM_STATE
+ valueFrom:
+ configMapKeyRef:
+ key: clean-cilium-state
+ name: cilium-config
+ optional: true
+ - name: CLEAN_CILIUM_BPF_STATE
+ valueFrom:
+ configMapKeyRef:
+ key: clean-cilium-bpf-state
+ name: cilium-config
+ optional: true
+ image: docker.io/cilium/cilium-init:2018-10-16
+ imagePullPolicy: IfNotPresent
+ name: clean-cilium-state
+ securityContext:
+ capabilities:
+ add:
+ - NET_ADMIN
+ privileged: true
+ volumeMounts:
+ - mountPath: /sys/fs/bpf
+ name: bpf-maps
+ - mountPath: /var/run/cilium
+ name: cilium-run
+ restartPolicy: Always
+ serviceAccount: cilium
+ serviceAccountName: cilium
+ terminationGracePeriodSeconds: 1
+ tolerations:
+ - operator: Exists
+ volumes:
+ # To keep state between restarts / upgrades
+ - hostPath:
+ path: /var/run/cilium
+ type: DirectoryOrCreate
+ name: cilium-run
+ # To keep state between restarts / upgrades for bpf maps
+ - hostPath:
+ path: /sys/fs/bpf
+ type: DirectoryOrCreate
+ name: bpf-maps
+ # To read docker events from the node
+ - hostPath:
+ path: /var/run/docker.sock
+ type: Socket
+ name: docker-socket
+ # To install cilium cni plugin in the host
+ - hostPath:
+ path: {{ bin_dir }}
+ type: DirectoryOrCreate
+ name: cni-path
+ # To install cilium cni configuration in the host
+ - hostPath:
+ path: /etc/cni/net.d
+ type: DirectoryOrCreate
+ name: etc-cni-netd
+ # To read the etcd config stored in config maps
+ - configMap:
+ defaultMode: 420
+ items:
+ - key: etcd-config
+ path: etcd.config
+ name: cilium-config
+ name: etcd-config-path
+ # To read the k8s etcd secrets in case the user might want to use TLS
+ - name: etcd-secrets
+ secret:
+ defaultMode: 420
+ optional: true
+ secretName: cilium-etcd-secrets
+ # To read the clustermesh configuration
+ - name: clustermesh-secrets
+ secret:
+ defaultMode: 420
+ optional: true
+ secretName: cilium-clustermesh
+ updateStrategy:
+ rollingUpdate:
+ # Specifies the maximum number of Pods that can be unavailable during the update process.
+ maxUnavailable: 2
+ type: RollingUpdate
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ labels:
+ io.cilium/app: operator
+ name: cilium-operator
+ name: cilium-operator
+ namespace: kube-system
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ io.cilium/app: operator
+ name: cilium-operator
+ strategy:
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 1
+ type: RollingUpdate
+ template:
+ metadata:
+ labels:
+ io.cilium/app: operator
+ name: cilium-operator
+ spec:
+ containers:
+ - args:
+ - --debug=$(CILIUM_DEBUG)
+ - --kvstore=etcd
+ - --kvstore-opt=etcd.config=/var/lib/etcd-config/etcd.config
+ command:
+ - cilium-operator
+ env:
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.namespace
+ - name: K8S_NODE_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: spec.nodeName
+ - name: CILIUM_DEBUG
+ valueFrom:
+ configMapKeyRef:
+ key: debug
+ name: cilium-config
+ optional: true
+ - name: CILIUM_CLUSTER_NAME
+ valueFrom:
+ configMapKeyRef:
+ key: cluster-name
+ name: cilium-config
+ optional: true
+ - name: CILIUM_CLUSTER_ID
+ valueFrom:
+ configMapKeyRef:
+ key: cluster-id
+ name: cilium-config
+ optional: true
+ - name: CILIUM_DISABLE_ENDPOINT_CRD
+ valueFrom:
+ configMapKeyRef:
+ key: disable-endpoint-crd
+ name: cilium-config
+ optional: true
+ - name: AWS_ACCESS_KEY_ID
+ valueFrom:
+ secretKeyRef:
+ key: AWS_ACCESS_KEY_ID
+ name: cilium-aws
+ optional: true
+ - name: AWS_SECRET_ACCESS_KEY
+ valueFrom:
+ secretKeyRef:
+ key: AWS_SECRET_ACCESS_KEY
+ name: cilium-aws
+ optional: true
+ - name: AWS_DEFAULT_REGION
+ valueFrom:
+ secretKeyRef:
+ key: AWS_DEFAULT_REGION
+ name: cilium-aws
+ optional: true
+ image: docker.io/cilium/operator:{{ cilium_ver }}
+ imagePullPolicy: IfNotPresent
+ name: cilium-operator
+ volumeMounts:
+ - mountPath: /var/lib/etcd-config
+ name: etcd-config-path
+ readOnly: true
+ - mountPath: /var/lib/etcd-secrets
+ name: etcd-secrets
+ readOnly: true
+ dnsPolicy: ClusterFirst
+ restartPolicy: Always
+ serviceAccount: cilium-operator
+ serviceAccountName: cilium-operator
+ volumes:
+ # To read the etcd config stored in config maps
+ - configMap:
+ defaultMode: 420
+ items:
+ - key: etcd-config
+ path: etcd.config
+ name: cilium-config
+ name: etcd-config-path
+ # To read the k8s etcd secrets in case the user might want to use TLS
+ - name: etcd-secrets
+ secret:
+ defaultMode: 420
+ optional: true
+ secretName: cilium-etcd-secrets
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: cilium-operator
+ namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+ name: cilium-operator
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - pods
+ - deployments
+ - componentstatuses
+ verbs:
+ - '*'
+- apiGroups:
+ - ""
+ resources:
+ - services
+ - endpoints
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - cilium.io
+ resources:
+ - ciliumnetworkpolicies
+ - ciliumnetworkpolicies/status
+ - ciliumendpoints
+ - ciliumendpoints/status
+ verbs:
+ - '*'
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ name: cilium-operator
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cilium-operator
+subjects:
+- kind: ServiceAccount
+ name: cilium-operator
+ namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+ name: cilium-etcd-operator
+rules:
+- apiGroups:
+ - etcd.database.coreos.com
+ resources:
+ - etcdclusters
+ verbs:
+ - get
+ - delete
+ - create
+- apiGroups:
+ - apiextensions.k8s.io
+ resources:
+ - customresourcedefinitions
+ verbs:
+ - delete
+ - get
+ - create
+- apiGroups:
+ - ""
+ resources:
+ - deployments
+ verbs:
+ - delete
+ - create
+ - get
+ - update
+- apiGroups:
+ - ""
+ resources:
+ - pods
+ verbs:
+ - list
+ - delete
+ - get
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - delete
+ - create
+ - get
+ - update
+- apiGroups:
+ - ""
+ resources:
+ - componentstatuses
+ verbs:
+ - get
+- apiGroups:
+ - extensions
+ resources:
+ - deployments
+ verbs:
+ - delete
+ - create
+ - get
+ - update
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - get
+ - create
+ - delete
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ name: cilium-etcd-operator
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cilium-etcd-operator
+subjects:
+- kind: ServiceAccount
+ name: cilium-etcd-operator
+ namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+ name: etcd-operator
+rules:
+- apiGroups:
+ - etcd.database.coreos.com
+ resources:
+ - etcdclusters
+ - etcdbackups
+ - etcdrestores
+ verbs:
+ - '*'
+- apiGroups:
+ - apiextensions.k8s.io
+ resources:
+ - customresourcedefinitions
+ verbs:
+ - '*'
+- apiGroups:
+ - ""
+ resources:
+ - pods
+ - services
+ - endpoints
+ - persistentvolumeclaims
+ - events
+ - deployments
+ verbs:
+ - '*'
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - '*'
+- apiGroups:
+ - extensions
+ resources:
+ - deployments
+ verbs:
+ - create
+ - get
+ - list
+ - patch
+ - update
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - get
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ name: etcd-operator
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: etcd-operator
+subjects:
+- kind: ServiceAccount
+ name: cilium-etcd-sa
+ namespace: kube-system
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: cilium-etcd-operator
+ namespace: kube-system
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: cilium-etcd-sa
+ namespace: kube-system
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ labels:
+ io.cilium/app: etcd-operator
+ name: cilium-etcd-operator
+ name: cilium-etcd-operator
+ namespace: kube-system
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ io.cilium/app: etcd-operator
+ name: cilium-etcd-operator
+ strategy:
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 1
+ type: RollingUpdate
+ template:
+ metadata:
+ labels:
+ io.cilium/app: etcd-operator
+ name: cilium-etcd-operator
+ spec:
+ containers:
+ - command:
+ - /usr/bin/cilium-etcd-operator
+ env:
+ - name: CILIUM_ETCD_OPERATOR_CLUSTER_DOMAIN
+ value: cluster.local
+ - name: CILIUM_ETCD_OPERATOR_ETCD_CLUSTER_SIZE
+ value: "{{ ETCD_CLUSTER_SIZE }}"
+ - name: CILIUM_ETCD_OPERATOR_NAMESPACE
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.namespace
+ - name: CILIUM_ETCD_OPERATOR_POD_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: CILIUM_ETCD_OPERATOR_POD_UID
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.uid
+{% if NODE_WITH_MULTIPLE_NETWORKS == 'true' %}
+ # if hosts have multiple net interfaces, set following two ENVs
+ - name: KUBERNETES_SERVICE_HOST
+ value: "{{ MASTER_IP }}"
+ #value: "{{ KUBE_APISERVER.split(':')[1].lstrip('/') }}"
+ - name: KUBERNETES_SERVICE_PORT
+ value: "{{ KUBE_APISERVER.split(':')[2] }}"
+{% endif %}
+ image: docker.io/cilium/cilium-etcd-operator:v2.0.5
+ imagePullPolicy: IfNotPresent
+ name: cilium-etcd-operator
+ dnsPolicy: ClusterFirst
+ hostNetwork: true
+ restartPolicy: Always
+ serviceAccount: cilium-etcd-operator
+ serviceAccountName: cilium-etcd-operator
+ tolerations:
+ - operator: Exists
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ name: cilium
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cilium
+subjects:
+- kind: ServiceAccount
+ name: cilium
+ namespace: kube-system
+- apiGroup: rbac.authorization.k8s.io
+ kind: Group
+ name: system:nodes
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+ name: cilium
+rules:
+- apiGroups:
+ - networking.k8s.io
+ resources:
+ - networkpolicies
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - ""
+ resources:
+ - namespaces
+ - services
+ - nodes
+ - endpoints
+ - componentstatuses
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - ""
+ resources:
+ - pods
+ - nodes
+ verbs:
+ - get
+ - list
+ - watch
+ - update
+- apiGroups:
+ - extensions
+ resources:
+ - ingresses
+ verbs:
+ - create
+ - get
+ - list
+ - watch
+- apiGroups:
+ - apiextensions.k8s.io
+ resources:
+ - customresourcedefinitions
+ verbs:
+ - create
+ - get
+ - list
+ - watch
+ - update
+- apiGroups:
+ - cilium.io
+ resources:
+ - ciliumnetworkpolicies
+ - ciliumnetworkpolicies/status
+ - ciliumendpoints
+ - ciliumendpoints/status
+ verbs:
+ - '*'
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: cilium
+ namespace: kube-system
diff --git a/roles/cluster-addon/defaults/main.yml b/roles/cluster-addon/defaults/main.yml
new file mode 100644
index 0000000..b66a3de
--- /dev/null
+++ b/roles/cluster-addon/defaults/main.yml
@@ -0,0 +1,42 @@
+# dns 自动安装,可选"coredns"和“kubedns”
+dns_install: "yes"
+dns_backend: "coredns"
+kubedns_offline: "kubedns_1.14.13.tar"
+coredns_offline: "coredns_1.2.6.tar"
+dns_offline: "{%- if dns_backend == 'coredns' -%} \
+ {{ coredns_offline }} \
+ {%- else -%} \
+ {{ kubedns_offline }} \
+ {%- endif -%}"
+
+# metric server 自动安装
+metricsserver_install: "yes"
+metricsserver_offline: "metrics-server_v0.3.1.tar"
+
+# dashboard 自动安装
+dashboard_install: "yes"
+dashboard_offline: "dashboard_v1.10.0.tar"
+
+# ingress 自动安装,可选 "traefik" 和 "nginx-ingress"
+ingress_install: "no"
+ingress_backend: "traefik"
+traefik_offline: "traefik_v1.7.4.tar"
+nginx_ingress_offline: "nginx_ingress_0.21.0.tar"
+
+# heapster 自动安装
+heapster_install: "no"
+heapster_offline: "heapster_v1.5.4.tar"
+
+# metallb 自动安装
+metallb_install: "no"
+# 模式选择: 二层 "layer2" 或者三层 "bgp"
+metallb_protocol: "layer2"
+metallb_offline: "metallb_v0.7.3.tar"
+metallb_vip_pool: "192.168.1.240/29"
+
+# efk 自动安装
+#efk_install: "no"
+
+# prometheus 自动安装
+#prometheus_install: "no"
+
diff --git a/roles/cluster-addon/tasks/ingress.yml b/roles/cluster-addon/tasks/ingress.yml
new file mode 100644
index 0000000..aa658db
--- /dev/null
+++ b/roles/cluster-addon/tasks/ingress.yml
@@ -0,0 +1,39 @@
+- block:
+ - name: 尝试推送离线 traefik镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ traefik_offline }} dest=/opt/kube/images/{{ traefik_offline }}
+ when: 'traefik_offline in download_info.stdout'
+
+ - name: 获取traefik离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+ - name: 导入 traefik的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ traefik_offline }}"
+ when: 'traefik_offline in image_info.stdout'
+
+ - name: 创建 traefik部署
+ shell: "{{ bin_dir }}/kubectl apply -f {{ base_dir }}/manifests/ingress/traefik/traefik-ingress.yaml"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+ when: 'ingress_backend == "traefik"'
+ ignore_errors: true
+
+- block:
+ - name: 尝试推送离线 nginx-ingress镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ nginx_ingress_offline }} dest=/opt/kube/images/{{ nginx_ingress_offline }}
+ when: 'nginx_ingress_offline in download_info.stdout'
+
+ - name: 获取nginx_ingress离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+ - name: 导入 nginx_ingress的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ nginx_ingress_offline }}"
+ when: 'nginx_ingress_offline in image_info.stdout'
+
+ - name: 创建 nginx_ingress部署
+ shell: "{{ bin_dir }}/kubectl apply -f {{ base_dir }}/manifests/ingress/nginx-ingress/nginx-ingress.yaml"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+ when: 'ingress_backend == "nginx-ingress"'
+ ignore_errors: true
diff --git a/roles/cluster-addon/tasks/main.yml b/roles/cluster-addon/tasks/main.yml
new file mode 100644
index 0000000..2dc2c7a
--- /dev/null
+++ b/roles/cluster-addon/tasks/main.yml
@@ -0,0 +1,145 @@
+- block:
+ - name: 在deploy 节点创建相关目录
+ file: path={{ item }} state=directory
+ with_items:
+ - /opt/kube/kube-system/kubedns
+ - /opt/kube/kube-system/coredns
+
+ # DNS文件中部分参数根据hosts文件设置而定,因此需要用template模块替换参数
+ - name: 准备 DNS的部署文件
+ template: src={{ item }}.yaml.j2 dest=/opt/kube/kube-system/{{ item }}/{{ item }}.yaml
+ with_items:
+ - kubedns
+ - coredns
+
+ - name: 获取所有已经创建的POD信息
+ command: "{{ bin_dir }}/kubectl get pod --all-namespaces"
+ register: pod_info
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+- name: 获取已下载离线镜像信息
+ command: "ls {{ base_dir }}/down"
+ register: download_info
+ connection: local
+ run_once: true
+
+- block:
+ - name: 尝试推送离线{{ dns_backend }}镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ dns_offline }} dest=/opt/kube/images/{{ dns_offline }}
+ when: 'dns_offline in download_info.stdout'
+
+ - name: 获取{{ dns_backend }}离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+ - name: 导入{{ dns_backend }}的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ dns_offline }}"
+ when: 'dns_offline in image_info.stdout'
+
+ - name: 创建{{ dns_backend }}部署
+ shell: "{{ bin_dir }}/kubectl apply -f /opt/kube/kube-system/{{ dns_backend }}"
+ run_once: true
+ delegate_to: "{{ groups.deploy[0] }}"
+ when:
+ - '"kube-dns" not in pod_info.stdout'
+ - '"coredns" not in pod_info.stdout'
+ - 'dns_install == "yes"'
+ ignore_errors: true
+
+- block:
+ - name: 尝试推送离线 metrics-server镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ metricsserver_offline }} dest=/opt/kube/images/{{ metricsserver_offline }}
+ when: 'metricsserver_offline in download_info.stdout'
+
+ - name: 获取metrics-server离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+ - name: 导入 metrics-server的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ metricsserver_offline }}"
+ when: 'metricsserver_offline in image_info.stdout'
+
+ - name: 创建 metrics-server部署
+ shell: "{{ bin_dir }}/kubectl apply -f {{ base_dir }}/manifests/metrics-server"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+ when: '"metrics-server" not in pod_info.stdout and metricsserver_install == "yes"'
+ ignore_errors: true
+
+- block:
+ - name: 尝试推送离线 dashboard镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ dashboard_offline }} dest=/opt/kube/images/{{ dashboard_offline }}
+ when: 'dashboard_offline in download_info.stdout'
+
+ - name: 获取dashboard离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+ - name: 导入 dashboard的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ dashboard_offline }}"
+ when: 'dashboard_offline in image_info.stdout'
+
+ - name: 创建 dashboard部署
+ shell: "{{ bin_dir }}/kubectl apply -f {{ base_dir }}/manifests/dashboard"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+ when: '"kubernetes-dashboard" not in pod_info.stdout and dashboard_install == "yes"'
+ ignore_errors: true
+
+- import_tasks: ingress.yml
+ when: '"ingress-controller" not in pod_info.stdout and ingress_install == "yes"'
+
+- block:
+ - name: 尝试推送离线 heapster镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ heapster_offline }} dest=/opt/kube/images/{{ heapster_offline }}
+ when: 'heapster_offline in download_info.stdout'
+
+ - name: 获取heapster离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+ - name: 导入 heapster的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ heapster_offline }}"
+ when: 'heapster_offline in image_info.stdout'
+
+ - name: 创建 heapster部署
+ shell: "{{ bin_dir }}/kubectl apply -f {{ base_dir }}/manifests/heapster/heapster.yaml"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+ when: '"heapster" not in pod_info.stdout and heapster_install == "yes"'
+ ignore_errors: true
+
+- block:
+ - name: 尝试推送离线 metallb镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ metallb_offline }} dest=/opt/kube/images/{{ metallb_offline }}
+ when: 'metallb_offline in download_info.stdout'
+
+ - name: 获取metallb离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+ - name: 导入 metallb的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ metallb_offline }}"
+ when: 'metallb_offline in image_info.stdout'
+
+ - name: 生成 metallb 相关 manifests
+ template: src=metallb/{{ item }}.j2 dest=/opt/kube/kube-system/{{ item }}
+ with_items:
+ - "metallb.yaml"
+ - "{{ metallb_protocol }}.yaml"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+ - name: 创建 metallb controller 部署
+ shell: "{{ bin_dir }}/kubectl apply -f /opt/kube/kube-system/metallb.yaml"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+ - name: 创建 metallb configmap
+ shell: "{{ bin_dir }}/kubectl apply -f /opt/kube/kube-system/{{ metallb_protocol }}.yaml"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+ when: '"metallb" not in pod_info.stdout and metallb_install == "yes"'
+ ignore_errors: true
+
diff --git a/roles/cluster-addon/templates/coredns.yaml.j2 b/roles/cluster-addon/templates/coredns.yaml.j2
new file mode 100644
index 0000000..9e57bb2
--- /dev/null
+++ b/roles/cluster-addon/templates/coredns.yaml.j2
@@ -0,0 +1,188 @@
+# __MACHINE_GENERATED_WARNING__
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: coredns
+ namespace: kube-system
+ labels:
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ kubernetes.io/bootstrapping: rbac-defaults
+ addonmanager.kubernetes.io/mode: Reconcile
+ name: system:coredns
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - endpoints
+ - services
+ - pods
+ - namespaces
+ verbs:
+ - list
+ - watch
+- apiGroups:
+ - ""
+ resources:
+ - nodes
+ verbs:
+ - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ annotations:
+ rbac.authorization.kubernetes.io/autoupdate: "true"
+ labels:
+ kubernetes.io/bootstrapping: rbac-defaults
+ addonmanager.kubernetes.io/mode: EnsureExists
+ name: system:coredns
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: system:coredns
+subjects:
+- kind: ServiceAccount
+ name: coredns
+ namespace: kube-system
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: coredns
+ namespace: kube-system
+ labels:
+ addonmanager.kubernetes.io/mode: EnsureExists
+data:
+ Corefile: |
+ .:53 {
+ errors
+ health
+ kubernetes {{ CLUSTER_DNS_DOMAIN }} in-addr.arpa ip6.arpa {
+ pods insecure
+ upstream
+ fallthrough in-addr.arpa ip6.arpa
+ }
+ prometheus :9153
+ proxy . /etc/resolv.conf
+ cache 30
+ loop
+ reload
+ loadbalance
+ }
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: coredns
+ namespace: kube-system
+ labels:
+ k8s-app: kube-dns
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+ kubernetes.io/name: "CoreDNS"
+spec:
+ replicas: 2
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxUnavailable: 1
+ selector:
+ matchLabels:
+ k8s-app: kube-dns
+ template:
+ metadata:
+ labels:
+ k8s-app: kube-dns
+ annotations:
+ seccomp.security.alpha.kubernetes.io/pod: 'docker/default'
+ spec:
+ serviceAccountName: coredns
+ tolerations:
+ - key: "CriticalAddonsOnly"
+ operator: "Exists"
+ containers:
+ - name: coredns
+ image: coredns/coredns:1.2.6
+ imagePullPolicy: IfNotPresent
+ resources:
+ limits:
+ memory: 170Mi
+ requests:
+ cpu: 100m
+ memory: 70Mi
+ args: [ "-conf", "/etc/coredns/Corefile" ]
+ volumeMounts:
+ - name: config-volume
+ mountPath: /etc/coredns
+ readOnly: true
+ ports:
+ - containerPort: 53
+ name: dns
+ protocol: UDP
+ - containerPort: 53
+ name: dns-tcp
+ protocol: TCP
+ - containerPort: 9153
+ name: metrics
+ protocol: TCP
+ livenessProbe:
+ httpGet:
+ path: /health
+ port: 8080
+ scheme: HTTP
+ initialDelaySeconds: 60
+ timeoutSeconds: 5
+ successThreshold: 1
+ failureThreshold: 5
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ add:
+ - NET_BIND_SERVICE
+ drop:
+ - all
+ readOnlyRootFilesystem: true
+ dnsPolicy: Default
+ volumes:
+ - name: config-volume
+ configMap:
+ name: coredns
+ items:
+ - key: Corefile
+ path: Corefile
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: kube-dns
+ namespace: kube-system
+ annotations:
+ prometheus.io/port: "9153"
+ prometheus.io/scrape: "true"
+ labels:
+ k8s-app: kube-dns
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+ kubernetes.io/name: "CoreDNS"
+spec:
+ selector:
+ k8s-app: kube-dns
+ clusterIP: {{ CLUSTER_DNS_SVC_IP }}
+ ports:
+ - name: dns
+ port: 53
+ protocol: UDP
+ - name: dns-tcp
+ port: 53
+ protocol: TCP
+ - name: metrics
+ port: 9153
+ protocol: TCP
+ targetPort: 9153
diff --git a/roles/cluster-addon/templates/kubedns.yaml.j2 b/roles/cluster-addon/templates/kubedns.yaml.j2
new file mode 100644
index 0000000..1bb13a7
--- /dev/null
+++ b/roles/cluster-addon/templates/kubedns.yaml.j2
@@ -0,0 +1,198 @@
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: kube-dns
+ namespace: kube-system
+ labels:
+ addonmanager.kubernetes.io/mode: EnsureExists
+
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: kube-dns
+ namespace: kube-system
+ labels:
+ #kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: kube-dns
+ namespace: kube-system
+ labels:
+ k8s-app: kube-dns
+ #kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+ kubernetes.io/name: "KubeDNS"
+spec:
+ selector:
+ k8s-app: kube-dns
+ clusterIP: {{ CLUSTER_DNS_SVC_IP }}
+ ports:
+ - name: dns
+ port: 53
+ protocol: UDP
+ - name: dns-tcp
+ port: 53
+ protocol: TCP
+
+---
+apiVersion: apps/v1beta1
+kind: Deployment
+metadata:
+ name: kube-dns
+ namespace: kube-system
+ labels:
+ k8s-app: kube-dns
+ #kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+spec:
+ # replicas: not specified here:
+ # 1. In order to make Addon Manager do not reconcile this replicas parameter.
+ # 2. Default is 1.
+ # 3. Will be tuned in real time if DNS horizontal auto-scaling is turned on.
+ strategy:
+ rollingUpdate:
+ maxSurge: 10%
+ maxUnavailable: 0
+ selector:
+ matchLabels:
+ k8s-app: kube-dns
+ template:
+ metadata:
+ labels:
+ k8s-app: kube-dns
+ annotations:
+ scheduler.alpha.kubernetes.io/critical-pod: ''
+ spec:
+ tolerations:
+ - key: "CriticalAddonsOnly"
+ operator: "Exists"
+ volumes:
+ - name: kube-dns-config
+ configMap:
+ name: kube-dns
+ optional: true
+ containers:
+ - name: kubedns
+ #image: gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.14.13
+ image: mirrorgooglecontainers/k8s-dns-kube-dns-amd64:1.14.13
+ resources:
+ # TODO: Set memory limits when we've profiled the container for large
+ # clusters, then set request = limit to keep this container in
+ # guaranteed class. Currently, this container falls into the
+ # "burstable" category so the kubelet doesn't backoff from restarting it.
+ limits:
+ memory: 170Mi
+ requests:
+ cpu: 100m
+ memory: 70Mi
+ livenessProbe:
+ httpGet:
+ path: /healthcheck/kubedns
+ port: 10054
+ scheme: HTTP
+ initialDelaySeconds: 60
+ timeoutSeconds: 5
+ successThreshold: 1
+ failureThreshold: 5
+ readinessProbe:
+ httpGet:
+ path: /readiness
+ port: 8081
+ scheme: HTTP
+ # we poll on pod startup for the Kubernetes master service and
+ # only setup the /readiness HTTP server once that's available.
+ initialDelaySeconds: 3
+ timeoutSeconds: 5
+ args:
+ - --domain={{ CLUSTER_DNS_DOMAIN }}
+ - --dns-port=10053
+ - --config-dir=/kube-dns-config
+ - --v=2
+ env:
+ - name: PROMETHEUS_PORT
+ value: "10055"
+ ports:
+ - containerPort: 10053
+ name: dns-local
+ protocol: UDP
+ - containerPort: 10053
+ name: dns-tcp-local
+ protocol: TCP
+ - containerPort: 10055
+ name: metrics
+ protocol: TCP
+ volumeMounts:
+ - name: kube-dns-config
+ mountPath: /kube-dns-config
+ - name: dnsmasq
+ #image: gcr.io/google_containers/k8s-dns-dnsmasq-nanny-amd64:1.14.13
+ image: mirrorgooglecontainers/k8s-dns-dnsmasq-nanny-amd64:1.14.13
+ livenessProbe:
+ httpGet:
+ path: /healthcheck/dnsmasq
+ port: 10054
+ scheme: HTTP
+ initialDelaySeconds: 60
+ timeoutSeconds: 5
+ successThreshold: 1
+ failureThreshold: 5
+ args:
+ - -v=2
+ - -logtostderr
+ - -configDir=/etc/k8s/dns/dnsmasq-nanny
+ - -restartDnsmasq=true
+ - --
+ - -k
+ - --cache-size=1000
+ - --log-facility=-
+ - --server=/{{ CLUSTER_DNS_DOMAIN }}/127.0.0.1#10053
+ - --server=/in-addr.arpa/127.0.0.1#10053
+ - --server=/ip6.arpa/127.0.0.1#10053
+ ports:
+ - containerPort: 53
+ name: dns
+ protocol: UDP
+ - containerPort: 53
+ name: dns-tcp
+ protocol: TCP
+ # see: https://github.com/kubernetes/kubernetes/issues/29055 for details
+ resources:
+ requests:
+ cpu: 150m
+ memory: 20Mi
+ volumeMounts:
+ - name: kube-dns-config
+ mountPath: /etc/k8s/dns/dnsmasq-nanny
+ - name: sidecar
+ #image: gcr.io/google_containers/k8s-dns-sidecar-amd64:1.14.13
+ image: mirrorgooglecontainers/k8s-dns-sidecar-amd64:1.14.13
+ livenessProbe:
+ httpGet:
+ path: /metrics
+ port: 10054
+ scheme: HTTP
+ initialDelaySeconds: 60
+ timeoutSeconds: 5
+ successThreshold: 1
+ failureThreshold: 5
+ args:
+ - --v=2
+ - --logtostderr
+ - --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.{{ CLUSTER_DNS_DOMAIN }},5,A
+ - --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.{{ CLUSTER_DNS_DOMAIN }},5,A
+ ports:
+ - containerPort: 10054
+ name: metrics
+ protocol: TCP
+ resources:
+ requests:
+ memory: 20Mi
+ cpu: 10m
+ dnsPolicy: Default # Don't use cluster DNS.
+ serviceAccountName: kube-dns
diff --git a/roles/cluster-addon/templates/metallb/bgp.yaml.j2 b/roles/cluster-addon/templates/metallb/bgp.yaml.j2
new file mode 100644
index 0000000..0d2d34e
--- /dev/null
+++ b/roles/cluster-addon/templates/metallb/bgp.yaml.j2
@@ -0,0 +1,113 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ namespace: metallb-system
+ name: config
+data:
+ config: |
+ # The peers section tells MetalLB what BGP routers to connect too. There
+ # is one entry for each router you want to peer with.
+ peers:
+ - # The target IP address for the BGP session.
+ peer-address: 10.0.0.1
+ # The BGP AS number that MetalLB expects to see advertised by
+ # the router.
+ peer-asn: 64512
+ # The BGP AS number that MetalLB should speak as.
+ my-asn: 64512
+ # (optional) the TCP port to talk to. Defaults to 179, you shouldn't
+ # need to set this in production.
+ peer-port: 179
+ # (optional) The proposed value of the BGP Hold Time timer. Refer to
+ # BGP reference material to understand what setting this implies.
+ hold-time: 120
+ # (optional) The router ID to use when connecting to this peer. Defaults
+ # to the node IP address. Generally only useful when you need to peer with
+ # another BGP router running on the same machine as MetalLB.
+ router-id: 1.2.3.4
+ # (optional) Password for TCPMD5 authenticated BGP sessions
+ # offered by some peers.
+ password: "yourPassword"
+ # (optional) The nodes that should connect to this peer. A node
+ # matches if at least one of the node selectors matches. Within
+ # one selector, a node matches if all the matchers are
+ # satisfied. The semantics of each selector are the same as the
+ # label- and set-based selectors in Kubernetes, documented at
+ # https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/.
+ # By default, all nodes are selected.
+ node-selectors:
+ - # Match by label=value
+ match-labels:
+ kubernetes.io/hostname: prod-01
+ # Match by 'key OP values' expressions
+ match-expressions:
+ - key: beta.kubernetes.io/arch
+ operator: In
+ values: [amd64, arm]
+
+ # The address-pools section lists the IP addresses that MetalLB is
+ # allowed to allocate, along with settings for how to advertise
+ # those addresses over BGP once assigned. You can have as many
+ # address pools as you want.
+ address-pools:
+ - # A name for the address pool. Services can request allocation
+ # from a specific address pool using this name, by listing this
+ # name under the 'metallb.universe.tf/address-pool' annotation.
+ name: my-ip-space
+ # Protocol can be used to select how the announcement is done.
+ # Supported values are bgp and layer2.
+ protocol: bgp
+
+ # A list of IP address ranges over which MetalLB has
+ # authority. You can list multiple ranges in a single pool, they
+ # will all share the same settings. Each range can be either a
+ # CIDR prefix, or an explicit start-end range of IPs.
+ addresses:
+ - 198.51.100.0/24
+ - 192.168.0.150-192.168.0.200
+ # (optional) If true, MetalLB will not allocate any address that
+ # ends in .0 or .255. Some old, buggy consumer devices
+ # mistakenly block traffic to such addresses under the guise of
+ # smurf protection. Such devices have become fairly rare, but
+ # the option is here if you encounter serving issues.
+ avoid-buggy-ips: true
+ # (optional, default true) If false, MetalLB will not automatically
+ # allocate any address in this pool. Addresses can still explicitly
+ # be requested via loadBalancerIP or the address-pool annotation.
+ auto-assign: false
+ # (optional) A list of BGP advertisements to make, when
+ # protocol=bgp. Each address that gets assigned out of this pool
+ # will turn into this many advertisements. For most simple
+ # setups, you'll probably just want one.
+ #
+ # The default value for this field is a single advertisement with
+ # all parameters set to their respective defaults.
+ bgp-advertisements:
+ - # (optional) How much you want to aggregate up the IP address
+ # before advertising. For example, advertising 1.2.3.4 with
+ # aggregation-length=24 would end up advertising 1.2.3.0/24.
+ # For the majority of setups, you'll want to keep this at the
+ # default of 32, which advertises the entire IP address
+ # unmodified.
+ aggregation-length: 32
+ # (optional) The value of the BGP "local preference" attribute
+ # for this advertisement. Only used with IBGP peers,
+ # i.e. peers where peer-asn is the same as my-asn.
+ localpref: 100
+ # (optional) BGP communities to attach to this
+ # advertisement. Communities are given in the standard
+ # two-part form :. You can also use
+ # alias names (see below).
+ communities:
+ - 64512:1
+ - no-export
+ # (optional) BGP community aliases. Instead of using hard to
+ # read BGP community numbers in address pool advertisement
+ # configurations, you can define alias names here and use those
+ # elsewhere in the configuration. The "no-export" community used
+ # above is defined below.
+ bgp-communities:
+ # no-export is a well-known BGP community that prevents
+ # re-advertisement outside of the immediate autonomous system,
+ # but people don't usually recognize its numerical value. :)
+ no-export: 65535:65281
diff --git a/roles/cluster-addon/templates/metallb/layer2.yaml.j2 b/roles/cluster-addon/templates/metallb/layer2.yaml.j2
new file mode 100644
index 0000000..426adde
--- /dev/null
+++ b/roles/cluster-addon/templates/metallb/layer2.yaml.j2
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ namespace: metallb-system
+ name: config
+data:
+ config: |
+ address-pools:
+ - name: my-ip-space
+ protocol: layer2
+ addresses:
+ - {{ metallb_vip_pool }}
diff --git a/roles/cluster-addon/templates/metallb/metallb.yaml.j2 b/roles/cluster-addon/templates/metallb/metallb.yaml.j2
new file mode 100644
index 0000000..98dd2cb
--- /dev/null
+++ b/roles/cluster-addon/templates/metallb/metallb.yaml.j2
@@ -0,0 +1,232 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: metallb-system
+ labels:
+ app: metallb
+---
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ namespace: metallb-system
+ name: controller
+ labels:
+ app: metallb
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ namespace: metallb-system
+ name: speaker
+ labels:
+ app: metallb
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: metallb-system:controller
+ labels:
+ app: metallb
+rules:
+- apiGroups: [""]
+ resources: ["services"]
+ verbs: ["get", "list", "watch", "update"]
+- apiGroups: [""]
+ resources: ["services/status"]
+ verbs: ["update"]
+- apiGroups: [""]
+ resources: ["events"]
+ verbs: ["create", "patch"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: metallb-system:speaker
+ labels:
+ app: metallb
+rules:
+- apiGroups: [""]
+ resources: ["services", "endpoints", "nodes"]
+ verbs: ["get", "list", "watch"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ namespace: metallb-system
+ name: config-watcher
+ labels:
+ app: metallb
+rules:
+- apiGroups: [""]
+ resources: ["configmaps"]
+ verbs: ["get", "list", "watch"]
+- apiGroups: [""]
+ resources: ["events"]
+ verbs: ["create"]
+---
+
+## Role bindings
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: metallb-system:controller
+ labels:
+ app: metallb
+subjects:
+- kind: ServiceAccount
+ name: controller
+ namespace: metallb-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: metallb-system:controller
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: metallb-system:speaker
+ labels:
+ app: metallb
+subjects:
+- kind: ServiceAccount
+ name: speaker
+ namespace: metallb-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: metallb-system:speaker
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ namespace: metallb-system
+ name: config-watcher
+ labels:
+ app: metallb
+subjects:
+- kind: ServiceAccount
+ name: controller
+- kind: ServiceAccount
+ name: speaker
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: config-watcher
+---
+apiVersion: apps/v1beta2
+kind: DaemonSet
+metadata:
+ namespace: metallb-system
+ name: speaker
+ labels:
+ app: metallb
+ component: speaker
+spec:
+ selector:
+ matchLabels:
+ app: metallb
+ component: speaker
+ template:
+ metadata:
+ labels:
+ app: metallb
+ component: speaker
+ annotations:
+ prometheus.io/scrape: "true"
+ prometheus.io/port: "7472"
+ spec:
+ serviceAccountName: speaker
+ terminationGracePeriodSeconds: 0
+ hostNetwork: true
+ nodeSelector:
+ node-role.kubernetes.io/metallb-speaker: "true"
+ containers:
+ - name: speaker
+ image: metallb/speaker:v0.7.3
+ imagePullPolicy: IfNotPresent
+ args:
+ - --port=7472
+ - --config=config
+ env:
+ - name: METALLB_NODE_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.nodeName
+ - name: KUBERNETES_SERVICE_HOST
+ value: "{{ MASTER_IP }}"
+ #value: "{{ KUBE_APISERVER.split(':')[1].lstrip('/') }}"
+ - name: KUBERNETES_SERVICE_PORT
+ value: "{{ KUBE_APISERVER.split(':')[2] }}"
+ ports:
+ - name: monitoring
+ containerPort: 7472
+ resources:
+ limits:
+ cpu: 100m
+ memory: 100Mi
+
+ securityContext:
+ allowPrivilegeEscalation: false
+ readOnlyRootFilesystem: true
+ capabilities:
+ drop:
+ - all
+ add:
+ - net_raw
+
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+ namespace: metallb-system
+ name: controller
+ labels:
+ app: metallb
+ component: controller
+spec:
+ revisionHistoryLimit: 3
+ selector:
+ matchLabels:
+ app: metallb
+ component: controller
+ template:
+ metadata:
+ labels:
+ app: metallb
+ component: controller
+ annotations:
+ prometheus.io/scrape: "true"
+ prometheus.io/port: "7472"
+ spec:
+ serviceAccountName: controller
+ terminationGracePeriodSeconds: 0
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 65534 # nobody
+ containers:
+ - name: controller
+ image: metallb/controller:v0.7.3
+ imagePullPolicy: IfNotPresent
+ args:
+ - --port=7472
+ - --config=config
+ ports:
+ - name: monitoring
+ containerPort: 7472
+ resources:
+ limits:
+ cpu: 100m
+ memory: 100Mi
+
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - all
+ readOnlyRootFilesystem: true
+
+---
+
+
diff --git a/roles/cluster-backup/files/readme.md b/roles/cluster-backup/files/readme.md
new file mode 100644
index 0000000..a742c3d
--- /dev/null
+++ b/roles/cluster-backup/files/readme.md
@@ -0,0 +1,3 @@
+# 集群备份目录
+
+此目标保留作为k8s 集群备份使用。
diff --git a/roles/cluster-backup/tasks/main.yml b/roles/cluster-backup/tasks/main.yml
new file mode 100644
index 0000000..cbecb2a
--- /dev/null
+++ b/roles/cluster-backup/tasks/main.yml
@@ -0,0 +1,14 @@
+- name: 准备备份目录
+ file: name=/backup/k8s state=directory
+
+- name: 执行etcd 数据备份
+ shell: "cd /backup/k8s && \
+ ETCDCTL_API=3 {{ bin_dir }}/etcdctl snapshot save snapshot.db"
+
+- name: 获取etcd 数据备份
+ fetch:
+ src: /backup/k8s/snapshot.db
+ dest: /etc/ansible/roles/cluster-backup/files/
+ flat: yes
+ run_once: true
+
diff --git a/roles/cluster-restore/defaults/main.yml b/roles/cluster-restore/defaults/main.yml
new file mode 100644
index 0000000..75a9922
--- /dev/null
+++ b/roles/cluster-restore/defaults/main.yml
@@ -0,0 +1,6 @@
+# 指定需要恢复的 etcd 数据备份,默认使用最近的一次备份
+db_to_restore: "snapshot.db"
+
+# etcd 集群间通信的IP和端口, 根据etcd组成员自动生成
+TMP_NODES: "{% for h in groups['etcd'] %}{{ hostvars[h]['NODE_NAME'] }}=https://{{ h }}:2380,{% endfor %}"
+ETCD_NODES: "{{ TMP_NODES.rstrip(',') }}"
diff --git a/roles/cluster-restore/tasks/main.yml b/roles/cluster-restore/tasks/main.yml
new file mode 100644
index 0000000..498f11d
--- /dev/null
+++ b/roles/cluster-restore/tasks/main.yml
@@ -0,0 +1,30 @@
+- name: 停止ectd 服务
+ service: name=etcd state=stopped
+
+- name: 清除etcd 数据目录
+ file: name=/var/lib/etcd/member state=absent
+
+- name: 生成备份目录
+ file: name=/backup/k8s state=directory
+
+- name: 准备指定的备份etcd 数据
+ copy:
+ src: "{{ base_dir }}/roles/cluster-backup/files/snapshot/{{ db_to_restore }}"
+ dest: "/backup/k8s/snapshot.db"
+
+- name: 清理原备份出来数据
+ file: name=/backup/k8s/{{ NODE_NAME }}.etcd state=absent
+
+- name: etcd 数据恢复
+ shell: "cd /backup/k8s && \
+ ETCDCTL_API=3 {{ bin_dir }}/etcdctl snapshot restore snapshot.db \
+ --name {{ NODE_NAME }} \
+ --initial-cluster {{ ETCD_NODES }} \
+ --initial-cluster-token etcd-cluster-0 \
+ --initial-advertise-peer-urls https://{{ inventory_hostname }}:2380"
+
+- name: 恢复数据至etcd 数据目录
+ shell: "cp -rf /backup/k8s/{{ NODE_NAME }}.etcd/member /var/lib/etcd/"
+
+- name: 重启etcd 服务
+ service: name=etcd state=restarted
diff --git a/roles/cluster-storage/cluster-storage.yml b/roles/cluster-storage/cluster-storage.yml
new file mode 100644
index 0000000..c98daf6
--- /dev/null
+++ b/roles/cluster-storage/cluster-storage.yml
@@ -0,0 +1,3 @@
+- hosts: deploy
+ roles:
+ - cluster-storage
diff --git a/roles/cluster-storage/defaults/main.yml b/roles/cluster-storage/defaults/main.yml
new file mode 100644
index 0000000..2c6a9b9
--- /dev/null
+++ b/roles/cluster-storage/defaults/main.yml
@@ -0,0 +1,17 @@
+# 动态存储类型, 目前支持自建nfs和aliyun_nas
+storage:
+ # nfs server 参数
+ nfs:
+ enabled: "no"
+ server: "172.16.3.86"
+ server_path: "/data/nfs"
+ storage_class: "nfs-dynamic-class"
+ provisioner_name: "nfs-provisioner-01"
+
+ # aliyun_nas 参数
+ aliyun_nas:
+ enabled: "no"
+ server: "xxxxxxxxxxx.cn-hangzhou.nas.aliyuncs.com"
+ server_path: "/"
+ storage_class: "class-aliyun-nas-01"
+ controller_name: "aliyun-nas-controller-01"
diff --git a/roles/cluster-storage/files/alicloud-disk.yaml b/roles/cluster-storage/files/alicloud-disk.yaml
new file mode 100644
index 0000000..42a9b3d
--- /dev/null
+++ b/roles/cluster-storage/files/alicloud-disk.yaml
@@ -0,0 +1,99 @@
+kind: StorageClass
+apiVersion: storage.k8s.io/v1beta1
+metadata:
+ name: alicloud-disk-common
+provisioner: alicloud/disk
+parameters:
+ type: cloud
+---
+kind: StorageClass
+apiVersion: storage.k8s.io/v1beta1
+metadata:
+ name: alicloud-disk-efficiency
+provisioner: alicloud/disk
+parameters:
+ type: cloud_efficiency
+---
+kind: StorageClass
+apiVersion: storage.k8s.io/v1beta1
+metadata:
+ name: alicloud-disk-ssd
+provisioner: alicloud/disk
+parameters:
+ type: cloud_ssd
+---
+kind: StorageClass
+apiVersion: storage.k8s.io/v1beta1
+metadata:
+ name: alicloud-disk-available
+provisioner: alicloud/disk
+parameters:
+ type: available
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: alicloud-disk-controller-runner
+rules:
+ - apiGroups: [""]
+ resources: ["persistentvolumes"]
+ verbs: ["get", "list", "watch", "create", "delete"]
+ - apiGroups: [""]
+ resources: ["persistentvolumeclaims"]
+ verbs: ["get", "list", "watch", "update"]
+ - apiGroups: ["storage.k8s.io"]
+ resources: ["storageclasses"]
+ verbs: ["get", "list", "watch"]
+ - apiGroups: [""]
+ resources: ["events"]
+ verbs: ["list", "watch", "create", "update", "patch"]
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: alicloud-disk-controller
+ namespace: kube-system
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: run-alicloud-disk-controller
+subjects:
+ - kind: ServiceAccount
+ name: alicloud-disk-controller
+ namespace: kube-system
+roleRef:
+ kind: ClusterRole
+ name: alicloud-disk-controller-runner
+ apiGroup: rbac.authorization.k8s.io
+---
+kind: Deployment
+apiVersion: extensions/v1beta1
+metadata:
+ name: alicloud-disk-controller
+ namespace: kube-system
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ template:
+ metadata:
+ labels:
+ app: alicloud-disk-controller
+ spec:
+ serviceAccount: alicloud-disk-controller
+ containers:
+ - name: alicloud-disk-controller
+ image: registry.cn-hangzhou.aliyuncs.com/acs/alicloud-disk-controller:v1.9.3-ed710ce
+ volumeMounts:
+ - name: cloud-config
+ mountPath: /etc/kubernetes/
+ - name: logdir
+ mountPath: /var/log/alicloud/
+ volumes:
+ - name: cloud-config
+ hostPath:
+ path: /etc/kubernetes/
+ - name: logdir
+ hostPath:
+ path: /var/log/alicloud/
diff --git a/roles/cluster-storage/tasks/alicloud-nas.yml b/roles/cluster-storage/tasks/alicloud-nas.yml
new file mode 100644
index 0000000..35804f7
--- /dev/null
+++ b/roles/cluster-storage/tasks/alicloud-nas.yml
@@ -0,0 +1,18 @@
+- name: 准备alicloud-nas配置目录
+ file: name=/opt/kube/kube-system/storage/alicloud-nas state=directory
+
+- name: 生成alicloud-nas动态存储配置
+ template:
+ src: alicloud-nas/alicloud-nas.yaml.j2
+ dest: "/opt/kube/kube-system/storage/alicloud-nas/{{ storage.aliyun_nas.controller_name }}.yaml"
+
+#- name: 复制alicloud-disk配置
+# copy:
+# src: alicloud-disk.yaml
+# dest: "{{ base_dir }}/manifests/storage/alicloud-nas/alicloud-disk.yaml"
+
+#- name: 开始部署alicloud-disk存储
+# shell: "{{ bin_dir }}/kubectl apply -f {{ base_dir }}/manifests/storage/alicloud-nas/alicloud-disk.yaml"
+
+- name: 开始部署alicloud-nas动态存储
+ shell: "{{ bin_dir }}/kubectl apply -f /opt/kube/kube-system/storage/alicloud-nas/{{ storage.aliyun_nas.controller_name }}.yaml"
diff --git a/roles/cluster-storage/tasks/main.yml b/roles/cluster-storage/tasks/main.yml
new file mode 100644
index 0000000..e3d1c24
--- /dev/null
+++ b/roles/cluster-storage/tasks/main.yml
@@ -0,0 +1,6 @@
+- import_tasks: nfs-client.yml
+ when: 'storage.nfs.enabled == "yes"'
+
+- import_tasks: alicloud-nas.yml
+ when: 'storage.aliyun_nas.enabled == "yes"'
+
diff --git a/roles/cluster-storage/tasks/nfs-client.yml b/roles/cluster-storage/tasks/nfs-client.yml
new file mode 100644
index 0000000..07905d9
--- /dev/null
+++ b/roles/cluster-storage/tasks/nfs-client.yml
@@ -0,0 +1,10 @@
+- name: 准备nfs-client 配置目录
+ file: name={{ base_dir }}/manifests/storage/nfs state=directory
+
+- name: 生成nfs-client动态存储配置
+ template:
+ src: nfs/nfs-client-provisioner.yaml.j2
+ dest: "{{ base_dir }}/manifests/storage/nfs/{{ storage.nfs.provisioner_name }}.yaml"
+
+- name: 开始部署nfs-client动态存储
+ shell: "{{ bin_dir }}/kubectl apply -f {{ base_dir }}/manifests/storage/nfs/{{ storage.nfs.provisioner_name }}.yaml"
diff --git a/roles/cluster-storage/templates/alicloud-nas/alicloud-nas.yaml.j2 b/roles/cluster-storage/templates/alicloud-nas/alicloud-nas.yaml.j2
new file mode 100644
index 0000000..a675987
--- /dev/null
+++ b/roles/cluster-storage/templates/alicloud-nas/alicloud-nas.yaml.j2
@@ -0,0 +1,76 @@
+apiVersion: storage.k8s.io/v1
+kind: StorageClass
+metadata:
+ name: {{ storage.aliyun_nas.storage_class }}
+provisioner: alicloud/nas
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: alicloud-nas-controller
+ namespace: kube-system
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: alicloud-disk-controller-runner
+rules:
+ - apiGroups: [""]
+ resources: ["persistentvolumes"]
+ verbs: ["get", "list", "watch", "create", "delete"]
+ - apiGroups: [""]
+ resources: ["persistentvolumeclaims"]
+ verbs: ["get", "list", "watch", "update"]
+ - apiGroups: ["storage.k8s.io"]
+ resources: ["storageclasses"]
+ verbs: ["get", "list", "watch"]
+ - apiGroups: [""]
+ resources: ["events"]
+ verbs: ["list", "watch", "create", "update", "patch"]
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: run-alicloud-nas-controller
+subjects:
+ - kind: ServiceAccount
+ name: alicloud-nas-controller
+ namespace: kube-system
+roleRef:
+ kind: ClusterRole
+ name: alicloud-disk-controller-runner
+ apiGroup: rbac.authorization.k8s.io
+---
+kind: Deployment
+apiVersion: apps/v1beta1
+metadata:
+ name: {{ storage.aliyun_nas.controller_name }}
+ namespace: kube-system
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ template:
+ metadata:
+ labels:
+ app: {{ storage.aliyun_nas.controller_name }}
+ spec:
+ serviceAccount: alicloud-nas-controller
+ containers:
+ - name: alicloud-nas-controller
+ image: registry.cn-hangzhou.aliyuncs.com/acs/alicloud-nas-controller:v1.8.4
+ volumeMounts:
+ - mountPath: /persistentvolumes
+ name: nfs-client-root
+ env:
+ - name: PROVISIONER_NAME
+ value: alicloud/nas
+ - name: NFS_SERVER
+ value: {{ storage.aliyun_nas.server }}
+ - name: NFS_PATH
+ value: {{ storage.aliyun_nas.server_path }}
+ volumes:
+ - name: nfs-client-root
+ nfs:
+ server: {{ storage.aliyun_nas.server }}
+ path: {{ storage.aliyun_nas.server_path }}
diff --git a/roles/cluster-storage/templates/nfs/nfs-client-provisioner.yaml.j2 b/roles/cluster-storage/templates/nfs/nfs-client-provisioner.yaml.j2
new file mode 100644
index 0000000..1795b6c
--- /dev/null
+++ b/roles/cluster-storage/templates/nfs/nfs-client-provisioner.yaml.j2
@@ -0,0 +1,87 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: nfs-client-provisioner
+ namespace: kube-system
+
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: nfs-client-provisioner-runner
+rules:
+ - apiGroups: [""]
+ resources: ["persistentvolumes"]
+ verbs: ["get", "list", "watch", "create", "delete"]
+ - apiGroups: [""]
+ resources: ["persistentvolumeclaims"]
+ verbs: ["get", "list", "watch", "update"]
+ - apiGroups: ["storage.k8s.io"]
+ resources: ["storageclasses"]
+ verbs: ["get", "list", "watch"]
+ - apiGroups: [""]
+ resources: ["events"]
+ verbs: ["list", "watch", "create", "update", "patch"]
+
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: run-nfs-client-provisioner
+subjects:
+ - kind: ServiceAccount
+ name: nfs-client-provisioner
+ namespace: kube-system
+roleRef:
+ kind: ClusterRole
+ name: nfs-client-provisioner-runner
+ apiGroup: rbac.authorization.k8s.io
+
+---
+kind: Deployment
+apiVersion: apps/v1beta1
+metadata:
+ name: {{ storage.nfs.provisioner_name }}
+ namespace: kube-system
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app: {{ storage.nfs.provisioner_name }}
+ template:
+ metadata:
+ labels:
+ app: {{ storage.nfs.provisioner_name }}
+ spec:
+ serviceAccountName: nfs-client-provisioner
+ containers:
+ - name: nfs-client-provisioner
+ #image: quay.io/external_storage/nfs-client-provisioner:latest
+ image: jmgao1983/nfs-client-provisioner:latest
+ imagePullPolicy: IfNotPresent
+ volumeMounts:
+ - name: nfs-client-root
+ mountPath: /persistentvolumes
+ env:
+ - name: PROVISIONER_NAME
+ # 此处供应者名字供storageclass调用
+ value: {{ storage.nfs.provisioner_name }}
+ - name: NFS_SERVER
+ value: {{ storage.nfs.server }}
+ - name: NFS_PATH
+ value: {{ storage.nfs.server_path }}
+ volumes:
+ - name: nfs-client-root
+ nfs:
+ server: {{ storage.nfs.server }}
+ path: {{ storage.nfs.server_path }}
+
+---
+apiVersion: storage.k8s.io/v1
+kind: StorageClass
+metadata:
+ name: {{ storage.nfs.storage_class }}
+provisioner: {{ storage.nfs.provisioner_name }}
+
diff --git a/roles/deploy/create-admin-kubeconfig.yml b/roles/deploy/create-admin-kubeconfig.yml
new file mode 100644
index 0000000..664b2b1
--- /dev/null
+++ b/roles/deploy/create-admin-kubeconfig.yml
@@ -0,0 +1,33 @@
+- hosts: deploy
+ tasks:
+ - name: 删除原有kubeconfig
+ file: path=/root/.kube state=absent
+
+ - name: 准备kubectl使用的admin 证书签名请求
+ template: src=admin-csr.json.j2 dest={{ ca_dir }}/admin-csr.json
+
+ - name: 创建 admin证书与私钥
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes admin-csr.json | {{ bin_dir }}/cfssljson -bare admin"
+ # 设置集群参数,指定CA证书和apiserver地址
+ - name: 设置集群参数
+ shell: "{{ bin_dir }}/kubectl config set-cluster kubernetes \
+ --certificate-authority={{ ca_dir }}/ca.pem \
+ --embed-certs=true \
+ --server={{ KUBE_APISERVER }}"
+ # 设置客户端认证参数,指定使用admin证书和私钥
+ - name: 设置客户端认证参数
+ shell: "{{ bin_dir }}/kubectl config set-credentials admin \
+ --client-certificate={{ ca_dir }}/admin.pem \
+ --embed-certs=true \
+ --client-key={{ ca_dir }}/admin-key.pem"
+ # 设置上下文参数,说明使用cluster集群和用户admin
+ - name: 设置上下文参数
+ shell: "{{ bin_dir }}/kubectl config set-context kubernetes \
+ --cluster=kubernetes --user=admin"
+ # 选择默认上下文
+ - name: 选择默认上下文
+ shell: "{{ bin_dir }}/kubectl config use-context kubernetes"
diff --git a/roles/deploy/create-read-kubeconfig.yml b/roles/deploy/create-read-kubeconfig.yml
new file mode 100644
index 0000000..e39404e
--- /dev/null
+++ b/roles/deploy/create-read-kubeconfig.yml
@@ -0,0 +1,44 @@
+- hosts: deploy
+ tasks:
+ - name: 在deploy 节点创建相关目录
+ file: path=/opt/kube/kube-system state=directory
+
+ - name: 下载 group:read rbac 文件
+ copy: src=read-group-rbac.yaml dest=/opt/kube/kube-system/read-group-rbac.yaml
+
+ - name: 创建group:read rbac 绑定
+ shell: "{{ bin_dir }}/kubectl apply -f /opt/kube/kube-system/read-group-rbac.yaml"
+
+ - name: 删除原有kubeconfig
+ file: path=/root/.kube state=absent
+
+ # 创建readonly kubectl kubeconfig文件: /root/.kube/config
+ - name: 准备kubectl使用的read 证书签名请求
+ template: src=read-csr.json.j2 dest={{ ca_dir }}/read-csr.json
+
+ - name: 创建 read证书与私钥
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes read-csr.json | {{ bin_dir }}/cfssljson -bare read"
+ # 设置集群参数,指定CA证书和apiserver地址
+ - name: 设置集群参数
+ shell: "{{ bin_dir }}/kubectl config set-cluster kubernetes \
+ --certificate-authority={{ ca_dir }}/ca.pem \
+ --embed-certs=true \
+ --server={{ KUBE_APISERVER }}"
+ # 设置客户端认证参数,指定使用read证书和私钥
+ - name: 设置客户端认证参数
+ shell: "{{ bin_dir }}/kubectl config set-credentials read \
+ --client-certificate={{ ca_dir }}/read.pem \
+ --embed-certs=true \
+ --client-key={{ ca_dir }}/read-key.pem"
+ # 设置上下文参数,说明使用cluster集群和用户read
+ - name: 设置上下文参数
+ shell: "{{ bin_dir }}/kubectl config set-context kubernetes \
+ --cluster=kubernetes --user=read"
+ # 选择默认上下文
+ - name: 选择默认上下文
+ shell: "{{ bin_dir }}/kubectl config use-context kubernetes"
+
diff --git a/roles/deploy/files/read-group-rbac.yaml b/roles/deploy/files/read-group-rbac.yaml
new file mode 100644
index 0000000..d39d85c
--- /dev/null
+++ b/roles/deploy/files/read-group-rbac.yaml
@@ -0,0 +1,142 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: read-clusterrole-binding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: read-clusterrole
+subjects:
+- kind: Group
+ name: "group:read"
+ apiGroup: rbac.authorization.k8s.io
+
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: read-clusterrole
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ - endpoints
+ - persistentvolumeclaims
+ - pods
+ - replicationcontrollers
+ - replicationcontrollers/scale
+ - serviceaccounts
+ - services
+ - nodes
+ - persistentvolumeclaims
+ - persistentvolumes
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - ""
+ resources:
+ - bindings
+ - events
+ - limitranges
+ - namespaces/status
+ - pods/log
+ - pods/status
+ - replicationcontrollers/status
+ - resourcequotas
+ - resourcequotas/status
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - ""
+ resources:
+ - namespaces
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - apps
+ resources:
+ - daemonsets
+ - deployments
+ - deployments/scale
+ - replicasets
+ - replicasets/scale
+ - statefulsets
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - autoscaling
+ resources:
+ - horizontalpodautoscalers
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - batch
+ resources:
+ - cronjobs
+ - jobs
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - extensions
+ resources:
+ - daemonsets
+ - deployments
+ - deployments/scale
+ - ingresses
+ - networkpolicies
+ - replicasets
+ - replicasets/scale
+ - replicationcontrollers/scale
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - policy
+ resources:
+ - poddisruptionbudgets
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - networking.k8s.io
+ resources:
+ - networkpolicies
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - storage.k8s.io
+ resources:
+ - storageclasses
+ - volumeattachments
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - rbac.authorization.k8s.io
+ resources:
+ - clusterrolebindings
+ - clusterroles
+ - roles
+ - rolebindings
+ verbs:
+ - get
+ - list
+ - watch
diff --git a/roles/deploy/tasks/main.yml b/roles/deploy/tasks/main.yml
new file mode 100644
index 0000000..744dfe4
--- /dev/null
+++ b/roles/deploy/tasks/main.yml
@@ -0,0 +1,130 @@
+- name: prepare some dirs
+ file: name={{ item }} state=directory
+ with_items:
+ - "{{ bin_dir }}"
+ - "{{ ca_dir }}"
+ - "{{ base_dir }}"
+ - "/etc/kubernetes"
+
+- name: 下载证书工具 CFSSL和 kubectl
+ copy: src={{ base_dir }}/bin/{{ item }} dest={{ bin_dir }}/{{ item }} mode=0755
+ with_items:
+ - cfssl
+ - cfssl-certinfo
+ - cfssljson
+ - kubectl
+
+# 注册变量p,根据p的stat信息判断是否已经生成过ca证书,如果没有,下一步生成证书
+# 如果已经有ca证书,为了保证整个安装的幂等性,跳过证书生成的步骤
+- name: 读取ca证书stat信息
+ stat: path="{{ ca_dir }}/ca.pem"
+ register: p
+
+- name: 准备CA配置文件
+ template: src=ca-config.json.j2 dest={{ ca_dir }}/ca-config.json
+ when: p.stat.isreg is not defined
+
+- name: 准备CA签名请求
+ template: src=ca-csr.json.j2 dest={{ ca_dir }}/ca-csr.json
+ when: p.stat.isreg is not defined
+
+- name: 生成 CA 证书和私钥
+ when: p.stat.isreg is not defined
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert -initca ca-csr.json | {{ bin_dir }}/cfssljson -bare ca"
+
+# 随机生成集群 basic auth 使用的密码
+- name: 生成随机 basic auth 密码
+ shell: 'export PWD=`date +%s%N | md5sum | head -c 16`; sed -i "s/^BASIC_AUTH_PASS.*$/BASIC_AUTH_PASS=\"$PWD\"/g" {{ base_dir }}/hosts'
+ connection: local
+ when: p.stat.isreg is not defined
+
+#----------- 创建kubectl kubeconfig文件: /root/.kube/config
+- name: 准备kubectl使用的admin 证书签名请求
+ template: src=admin-csr.json.j2 dest={{ ca_dir }}/admin-csr.json
+
+- name: 创建 admin证书与私钥
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes admin-csr.json | {{ bin_dir }}/cfssljson -bare admin"
+# 设置集群参数,指定CA证书和apiserver地址
+- name: 设置集群参数
+ shell: "{{ bin_dir }}/kubectl config set-cluster kubernetes \
+ --certificate-authority={{ ca_dir }}/ca.pem \
+ --embed-certs=true \
+ --server={{ KUBE_APISERVER }}"
+# 设置客户端认证参数,指定使用admin证书和私钥
+- name: 设置客户端认证参数
+ shell: "{{ bin_dir }}/kubectl config set-credentials admin \
+ --client-certificate={{ ca_dir }}/admin.pem \
+ --embed-certs=true \
+ --client-key={{ ca_dir }}/admin-key.pem"
+# 设置上下文参数,说明使用cluster集群和用户admin
+- name: 设置上下文参数
+ shell: "{{ bin_dir }}/kubectl config set-context kubernetes \
+ --cluster=kubernetes --user=admin"
+# 选择默认上下文
+- name: 选择默认上下文
+ shell: "{{ bin_dir }}/kubectl config use-context kubernetes"
+
+#------------创建kube-proxy.kubeconfig配置文件: /root/kube-proxy.kubeconfig
+- name: 准备kube-proxy 证书签名请求
+ template: src=kube-proxy-csr.json.j2 dest={{ ca_dir }}/kube-proxy-csr.json
+
+- name: 创建 kube-proxy证书与私钥
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes kube-proxy-csr.json | {{ bin_dir }}/cfssljson -bare kube-proxy"
+
+- name: 设置集群参数
+ shell: "{{ bin_dir }}/kubectl config set-cluster kubernetes \
+ --certificate-authority={{ ca_dir }}/ca.pem \
+ --embed-certs=true \
+ --server={{ KUBE_APISERVER }} \
+ --kubeconfig=kube-proxy.kubeconfig"
+- name: 设置客户端认证参数
+ shell: "{{ bin_dir }}/kubectl config set-credentials kube-proxy \
+ --client-certificate={{ ca_dir }}/kube-proxy.pem \
+ --client-key={{ ca_dir }}/kube-proxy-key.pem \
+ --embed-certs=true \
+ --kubeconfig=kube-proxy.kubeconfig"
+- name: 设置上下文参数
+ shell: "{{ bin_dir }}/kubectl config set-context default \
+ --cluster=kubernetes \
+ --user=kube-proxy \
+ --kubeconfig=kube-proxy.kubeconfig"
+- name: 选择默认上下文
+ shell: "{{ bin_dir }}/kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig"
+
+- name: 移动 kube-proxy.kubeconfig
+ shell: "mv /root/kube-proxy.kubeconfig /etc/kubernetes/"
+
+# --- 以下为兼容ansible执行节点与deploy节点分离情况-------
+- block:
+ - name: 在 ansible 执行节点创建 .kube 目录
+ file: path=/root/.kube state=directory
+
+ - name: 获取 kubeconfig 文件
+ fetch: src=/root/.kube/config dest=/root/.kube/config flat=yes
+
+ - name: 创建 kubectl 命令的软连接
+ file: src={{ base_dir }}/bin/kubectl dest=/usr/bin/kubectl state=link
+ connection: local
+
+ - name: 安装 rsync
+ package: name=rsync state=present
+
+ - name: 复制 manifests至 deploy节点
+ #copy: src={{ base_dir }}/manifests dest={{ base_dir }}
+ synchronize: src={{ base_dir }}/manifests dest={{ base_dir }}
+
+ - name: 推送 ssh 密钥对至 deploy节点
+ copy: src=~/.ssh/{{ item }} dest=~/.ssh/{{ item }} mode=0600
+ with_items:
+ - id_rsa
+ - known_hosts
+ run_once: true
+ when: "ansible_env['SSH_CLIENT'].split(' ')[0] != inventory_hostname"
diff --git a/roles/deploy/templates/admin-csr.json.j2 b/roles/deploy/templates/admin-csr.json.j2
new file mode 100644
index 0000000..a6b8388
--- /dev/null
+++ b/roles/deploy/templates/admin-csr.json.j2
@@ -0,0 +1,17 @@
+{
+ "CN": "admin",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "system:masters",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/deploy/templates/ca-config.json.j2 b/roles/deploy/templates/ca-config.json.j2
new file mode 100644
index 0000000..9f84cd3
--- /dev/null
+++ b/roles/deploy/templates/ca-config.json.j2
@@ -0,0 +1,18 @@
+{
+ "signing": {
+ "default": {
+ "expiry": "87600h"
+ },
+ "profiles": {
+ "kubernetes": {
+ "usages": [
+ "signing",
+ "key encipherment",
+ "server auth",
+ "client auth"
+ ],
+ "expiry": "87600h"
+ }
+ }
+ }
+}
diff --git a/roles/deploy/templates/ca-csr.json.j2 b/roles/deploy/templates/ca-csr.json.j2
new file mode 100644
index 0000000..f91944a
--- /dev/null
+++ b/roles/deploy/templates/ca-csr.json.j2
@@ -0,0 +1,19 @@
+{
+ "CN": "kubernetes",
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ],
+ "ca": {
+ "expiry": "131400h"
+ }
+}
diff --git a/roles/deploy/templates/kube-proxy-csr.json.j2 b/roles/deploy/templates/kube-proxy-csr.json.j2
new file mode 100644
index 0000000..0ee912d
--- /dev/null
+++ b/roles/deploy/templates/kube-proxy-csr.json.j2
@@ -0,0 +1,17 @@
+{
+ "CN": "system:kube-proxy",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/deploy/templates/read-csr.json.j2 b/roles/deploy/templates/read-csr.json.j2
new file mode 100644
index 0000000..e70221d
--- /dev/null
+++ b/roles/deploy/templates/read-csr.json.j2
@@ -0,0 +1,17 @@
+{
+ "CN": "read",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "group:read",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/docker/defaults/main.yml b/roles/docker/defaults/main.yml
new file mode 100644
index 0000000..82b3d54
--- /dev/null
+++ b/roles/docker/defaults/main.yml
@@ -0,0 +1,17 @@
+# 国内镜像加速
+
+REG_MIRROR_1: "https://registry.docker-cn.com"
+REG_MIRROR_2: "https://docker.mirrors.ustc.edu.cn"
+
+# docker日志相关
+
+LOG_DRIVER: "json-file"
+LOG_LEVEL: "warn"
+LOG_MAX_SIZE: "10m"
+LOG_MAX_FILE: 3
+
+# docker容器存储目录
+STORAGE_DIR: "/var/lib/docker"
+
+# 开启Restful API
+ENABLE_REMOTE_API: false
diff --git a/roles/docker/files/docker b/roles/docker/files/docker
new file mode 100644
index 0000000..78715d1
--- /dev/null
+++ b/roles/docker/files/docker
@@ -0,0 +1,4512 @@
+#!/usr/bin/env bash
+#
+# bash completion file for core docker commands
+#
+# This script provides completion of:
+# - commands and their options
+# - container ids and names
+# - image repos and tags
+# - filepaths
+#
+# To enable the completions either:
+# - place this file in /etc/bash_completion.d
+# or
+# - copy this file to e.g. ~/.docker-completion.sh and add the line
+# below to your .bashrc after bash completion features are loaded
+# . ~/.docker-completion.sh
+#
+# Configuration:
+#
+# For several commands, the amount of completions can be configured by
+# setting environment variables.
+#
+# DOCKER_COMPLETION_SHOW_CONTAINER_IDS
+# DOCKER_COMPLETION_SHOW_NETWORK_IDS
+# DOCKER_COMPLETION_SHOW_NODE_IDS
+# DOCKER_COMPLETION_SHOW_PLUGIN_IDS
+# DOCKER_COMPLETION_SHOW_SECRET_IDS
+# DOCKER_COMPLETION_SHOW_SERVICE_IDS
+# "no" - Show names only (default)
+# "yes" - Show names and ids
+#
+# You can tailor completion for the "events", "history", "inspect", "run",
+# "rmi" and "save" commands by settings the following environment
+# variables:
+#
+# DOCKER_COMPLETION_SHOW_IMAGE_IDS
+# "none" - Show names only (default)
+# "non-intermediate" - Show names and ids, but omit intermediate image IDs
+# "all" - Show names and ids, including intermediate image IDs
+#
+# DOCKER_COMPLETION_SHOW_TAGS
+# "yes" - include tags in completion options (default)
+# "no" - don't include tags in completion options
+
+#
+# Note:
+# Currently, the completions will not work if the docker daemon is not
+# bound to the default communication port/socket
+# If the docker daemon is using a unix socket for communication your user
+# must have access to the socket for the completions to function correctly
+#
+# Note for developers:
+# Please arrange options sorted alphabetically by long name with the short
+# options immediately following their corresponding long form.
+# This order should be applied to lists, alternatives and code blocks.
+
+__docker_previous_extglob_setting=$(shopt -p extglob)
+shopt -s extglob
+
+__docker_q() {
+ docker ${host:+-H "$host"} ${config:+--config "$config"} 2>/dev/null "$@"
+}
+
+# __docker_containers returns a list of containers. Additional options to
+# `docker ps` may be specified in order to filter the list, e.g.
+# `__docker_containers --filter status=running`
+# By default, only names are returned.
+# Set DOCKER_COMPLETION_SHOW_CONTAINER_IDS=yes to also complete IDs.
+# An optional first option `--id|--name` may be used to limit the
+# output to the IDs or names of matching items. This setting takes
+# precedence over the environment setting.
+__docker_containers() {
+ local format
+ if [ "$1" = "--id" ] ; then
+ format='{{.ID}}'
+ shift
+ elif [ "$1" = "--name" ] ; then
+ format='{{.Names}}'
+ shift
+ elif [ "${DOCKER_COMPLETION_SHOW_CONTAINER_IDS}" = yes ] ; then
+ format='{{.ID}} {{.Names}}'
+ else
+ format='{{.Names}}'
+ fi
+ __docker_q ps --format "$format" "$@"
+}
+
+# __docker_complete_containers applies completion of containers based on the current
+# value of `$cur` or the value of the optional first option `--cur`, if given.
+# Additional filters may be appended, see `__docker_containers`.
+__docker_complete_containers() {
+ local current="$cur"
+ if [ "$1" = "--cur" ] ; then
+ current="$2"
+ shift 2
+ fi
+ COMPREPLY=( $(compgen -W "$(__docker_containers "$@")" -- "$current") )
+}
+
+__docker_complete_containers_all() {
+ __docker_complete_containers "$@" --all
+}
+
+__docker_complete_containers_running() {
+ __docker_complete_containers "$@" --filter status=running
+}
+
+__docker_complete_containers_stopped() {
+ __docker_complete_containers "$@" --filter status=exited
+}
+
+__docker_complete_containers_unpauseable() {
+ __docker_complete_containers "$@" --filter status=paused
+}
+
+__docker_complete_container_names() {
+ local containers=( $(__docker_q ps -aq --no-trunc) )
+ local names=( $(__docker_q inspect --format '{{.Name}}' "${containers[@]}") )
+ names=( "${names[@]#/}" ) # trim off the leading "/" from the container names
+ COMPREPLY=( $(compgen -W "${names[*]}" -- "$cur") )
+}
+
+__docker_complete_container_ids() {
+ local containers=( $(__docker_q ps -aq) )
+ COMPREPLY=( $(compgen -W "${containers[*]}" -- "$cur") )
+}
+
+__docker_images() {
+ local images_args=""
+
+ case "$DOCKER_COMPLETION_SHOW_IMAGE_IDS" in
+ all)
+ images_args="--no-trunc -a"
+ ;;
+ non-intermediate)
+ images_args="--no-trunc"
+ ;;
+ esac
+
+ local repo_print_command
+ if [ "${DOCKER_COMPLETION_SHOW_TAGS:-yes}" = "yes" ]; then
+ repo_print_command='print $1; print $1":"$2'
+ else
+ repo_print_command='print $1'
+ fi
+
+ local awk_script
+ case "$DOCKER_COMPLETION_SHOW_IMAGE_IDS" in
+ all|non-intermediate)
+ awk_script='NR>1 { print $3; if ($1 != "") { '"$repo_print_command"' } }'
+ ;;
+ none|*)
+ awk_script='NR>1 && $1 != "" { '"$repo_print_command"' }'
+ ;;
+ esac
+
+ __docker_q images $images_args | awk "$awk_script" | grep -v '$'
+}
+
+__docker_complete_images() {
+ COMPREPLY=( $(compgen -W "$(__docker_images)" -- "$cur") )
+ __ltrim_colon_completions "$cur"
+}
+
+__docker_complete_image_repos() {
+ local repos="$(__docker_q images | awk 'NR>1 && $1 != "" { print $1 }')"
+ COMPREPLY=( $(compgen -W "$repos" -- "$cur") )
+}
+
+__docker_complete_image_repos_and_tags() {
+ local reposAndTags="$(__docker_q images | awk 'NR>1 && $1 != "" { print $1; print $1":"$2 }')"
+ COMPREPLY=( $(compgen -W "$reposAndTags" -- "$cur") )
+ __ltrim_colon_completions "$cur"
+}
+
+# __docker_networks returns a list of all networks. Additional options to
+# `docker network ls` may be specified in order to filter the list, e.g.
+# `__docker_networks --filter type=custom`
+# By default, only names are returned.
+# Set DOCKER_COMPLETION_SHOW_NETWORK_IDS=yes to also complete IDs.
+# An optional first option `--id|--name` may be used to limit the
+# output to the IDs or names of matching items. This setting takes
+# precedence over the environment setting.
+__docker_networks() {
+ local format
+ if [ "$1" = "--id" ] ; then
+ format='{{.ID}}'
+ shift
+ elif [ "$1" = "--name" ] ; then
+ format='{{.Name}}'
+ shift
+ elif [ "${DOCKER_COMPLETION_SHOW_NETWORK_IDS}" = yes ] ; then
+ format='{{.ID}} {{.Name}}'
+ else
+ format='{{.Name}}'
+ fi
+ __docker_q network ls --format "$format" "$@"
+}
+
+# __docker_complete_networks applies completion of networks based on the current
+# value of `$cur` or the value of the optional first option `--cur`, if given.
+# Additional filters may be appended, see `__docker_networks`.
+__docker_complete_networks() {
+ local current="$cur"
+ if [ "$1" = "--cur" ] ; then
+ current="$2"
+ shift 2
+ fi
+ COMPREPLY=( $(compgen -W "$(__docker_networks "$@")" -- "$current") )
+}
+
+__docker_complete_containers_in_network() {
+ local containers=$(__docker_q network inspect -f '{{range $i, $c := .Containers}}{{$i}} {{$c.Name}} {{end}}' "$1")
+ COMPREPLY=( $(compgen -W "$containers" -- "$cur") )
+}
+
+# __docker_volumes returns a list of all volumes. Additional options to
+# `docker volume ls` may be specified in order to filter the list, e.g.
+# `__docker_volumes --filter dangling=true`
+# Because volumes do not have IDs, this function does not distinguish between
+# IDs and names.
+__docker_volumes() {
+ __docker_q volume ls -q "$@"
+}
+
+# __docker_complete_volumes applies completion of volumes based on the current
+# value of `$cur` or the value of the optional first option `--cur`, if given.
+# Additional filters may be appended, see `__docker_volumes`.
+__docker_complete_volumes() {
+ local current="$cur"
+ if [ "$1" = "--cur" ] ; then
+ current="$2"
+ shift 2
+ fi
+ COMPREPLY=( $(compgen -W "$(__docker_volumes "$@")" -- "$current") )
+}
+
+# __docker_plugins_bundled returns a list of all plugins of a given type.
+# The type has to be specified with the mandatory option `--type`.
+# Valid types are: Network, Volume, Authorization.
+# Completions may be added or removed with `--add` and `--remove`
+# This function only deals with plugins that come bundled with Docker.
+# For plugins managed by `docker plugin`, see `__docker_plugins_installed`.
+__docker_plugins_bundled() {
+ local type add=() remove=()
+ while true ; do
+ case "$1" in
+ --type)
+ type="$2"
+ shift 2
+ ;;
+ --add)
+ add+=("$2")
+ shift 2
+ ;;
+ --remove)
+ remove+=("$2")
+ shift 2
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ local plugins=($(__docker_q info --format "{{range \$i, \$p := .Plugins.$type}}{{.}} {{end}}"))
+ for del in "${remove[@]}" ; do
+ plugins=(${plugins[@]/$del/})
+ done
+ echo "${plugins[@]} ${add[@]}"
+}
+
+# __docker_complete_plugins_bundled applies completion of plugins based on the current
+# value of `$cur` or the value of the optional first option `--cur`, if given.
+# The plugin type has to be specified with the next option `--type`.
+# This function only deals with plugins that come bundled with Docker.
+# For completion of plugins managed by `docker plugin`, see
+# `__docker_complete_plugins_installed`.
+__docker_complete_plugins_bundled() {
+ local current="$cur"
+ if [ "$1" = "--cur" ] ; then
+ current="$2"
+ shift 2
+ fi
+ COMPREPLY=( $(compgen -W "$(__docker_plugins_bundled "$@")" -- "$current") )
+}
+
+# __docker_plugins_installed returns a list of all plugins that were installed with
+# the Docker plugin API.
+# By default, only names are returned.
+# Set DOCKER_COMPLETION_SHOW_PLUGIN_IDS=yes to also complete IDs.
+# Additional options to `docker plugin ls` may be specified in order to filter the list,
+# e.g. `__docker_plugins_installed --filter enabled=true`
+# For built-in pugins, see `__docker_plugins_bundled`.
+__docker_plugins_installed() {
+ local format
+ if [ "$DOCKER_COMPLETION_SHOW_PLUGIN_IDS" = yes ] ; then
+ format='{{.ID}} {{.Name}}'
+ else
+ format='{{.Name}}'
+ fi
+ __docker_q plugin ls --format "$format" "$@"
+}
+
+# __docker_complete_plugins_installed applies completion of plugins that were installed
+# with the Docker plugin API, based on the current value of `$cur` or the value of
+# the optional first option `--cur`, if given.
+# Additional filters may be appended, see `__docker_plugins_installed`.
+# For completion of built-in pugins, see `__docker_complete_plugins_bundled`.
+__docker_complete_plugins_installed() {
+ local current="$cur"
+ if [ "$1" = "--cur" ] ; then
+ current="$2"
+ shift 2
+ fi
+ COMPREPLY=( $(compgen -W "$(__docker_plugins_installed "$@")" -- "$current") )
+}
+
+__docker_runtimes() {
+ __docker_q info | sed -n 's/^Runtimes: \(.*\)/\1/p'
+}
+
+__docker_complete_runtimes() {
+ COMPREPLY=( $(compgen -W "$(__docker_runtimes)" -- "$cur") )
+}
+
+# __docker_secrets returns a list of all secrets.
+# By default, only names of secrets are returned.
+# Set DOCKER_COMPLETION_SHOW_SECRET_IDS=yes to also complete IDs of secrets.
+__docker_secrets() {
+ local fields='$2' # default: name only
+ [ "${DOCKER_COMPLETION_SHOW_SECRET_IDS}" = yes ] && fields='$1,$2' # ID and name
+
+ __docker_q secret ls | awk "NR>1 {print $fields}"
+}
+
+# __docker_complete_secrets applies completion of secrets based on the current value
+# of `$cur`.
+__docker_complete_secrets() {
+ COMPREPLY=( $(compgen -W "$(__docker_secrets)" -- "$cur") )
+}
+
+# __docker_stacks returns a list of all stacks.
+__docker_stacks() {
+ __docker_q stack ls | awk 'NR>1 {print $1}'
+}
+
+# __docker_complete_stacks applies completion of stacks based on the current value
+# of `$cur` or the value of the optional first option `--cur`, if given.
+__docker_complete_stacks() {
+ local current="$cur"
+ if [ "$1" = "--cur" ] ; then
+ current="$2"
+ shift 2
+ fi
+ COMPREPLY=( $(compgen -W "$(__docker_stacks "$@")" -- "$current") )
+}
+
+# __docker_nodes returns a list of all nodes. Additional options to
+# `docker node ls` may be specified in order to filter the list, e.g.
+# `__docker_nodes --filter role=manager`
+# By default, only node names are returned.
+# Set DOCKER_COMPLETION_SHOW_NODE_IDS=yes to also complete node IDs.
+# An optional first option `--id|--name` may be used to limit the
+# output to the IDs or names of matching items. This setting takes
+# precedence over the environment setting.
+# Completions may be added with `--add`, e.g. `--add self`.
+__docker_nodes() {
+ local add=()
+ local fields='$2' # default: node name only
+ [ "${DOCKER_COMPLETION_SHOW_NODE_IDS}" = yes ] && fields='$1,$2' # ID and name
+
+ while true ; do
+ case "$1" in
+ --id)
+ fields='$1' # IDs only
+ shift
+ ;;
+ --name)
+ fields='$2' # names only
+ shift
+ ;;
+ --add)
+ add+=("$2")
+ shift 2
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ echo $(__docker_q node ls "$@" | tr -d '*' | awk "NR>1 {print $fields}") "${add[@]}"
+}
+
+# __docker_complete_nodes applies completion of nodes based on the current
+# value of `$cur` or the value of the optional first option `--cur`, if given.
+# Additional filters may be appended, see `__docker_nodes`.
+__docker_complete_nodes() {
+ local current="$cur"
+ if [ "$1" = "--cur" ] ; then
+ current="$2"
+ shift 2
+ fi
+ COMPREPLY=( $(compgen -W "$(__docker_nodes "$@")" -- "$current") )
+}
+
+# __docker_services returns a list of all services. Additional options to
+# `docker service ls` may be specified in order to filter the list, e.g.
+# `__docker_services --filter name=xxx`
+# By default, only node names are returned.
+# Set DOCKER_COMPLETION_SHOW_SERVICE_IDS=yes to also complete IDs.
+# An optional first option `--id|--name` may be used to limit the
+# output to the IDs or names of matching items. This setting takes
+# precedence over the environment setting.
+__docker_services() {
+ local fields='$2' # default: service name only
+ [ "${DOCKER_COMPLETION_SHOW_SERVICE_IDS}" = yes ] && fields='$1,$2' # ID & name
+
+ if [ "$1" = "--id" ] ; then
+ fields='$1' # IDs only
+ shift
+ elif [ "$1" = "--name" ] ; then
+ fields='$2' # names only
+ shift
+ fi
+ __docker_q service ls "$@" | awk "NR>1 {print $fields}"
+}
+
+# __docker_complete_services applies completion of services based on the current
+# value of `$cur` or the value of the optional first option `--cur`, if given.
+# Additional filters may be appended, see `__docker_services`.
+__docker_complete_services() {
+ local current="$cur"
+ if [ "$1" = "--cur" ] ; then
+ current="$2"
+ shift 2
+ fi
+ COMPREPLY=( $(compgen -W "$(__docker_services "$@")" -- "$current") )
+}
+
+# __docker_append_to_completions appends the word passed as an argument to every
+# word in `$COMPREPLY`.
+# Normally you do this with `compgen -S` while generating the completions.
+# This function allows you to append a suffix later. It allows you to use
+# the __docker_complete_XXX functions in cases where you need a suffix.
+__docker_append_to_completions() {
+ COMPREPLY=( ${COMPREPLY[@]/%/"$1"} )
+}
+
+# __docker_daemon_is_experimental tests whether the currently configured Docker
+# daemon runs in experimental mode. If so, the function exits with 0 (true).
+# Otherwise, or if the result cannot be determined, the exit value is 1 (false).
+__docker_daemon_is_experimental() {
+ [ "$(__docker_q version -f '{{.Server.Experimental}}')" = "true" ]
+}
+
+# __docker_daemon_os_is tests whether the currently configured Docker daemon runs
+# on the operating system passed in as the first argument.
+# It does so by querying the daemon for its OS. The result is cached for the duration
+# of one invocation of bash completion so that this function can be used to test for
+# several different operating systems without additional costs.
+# Known operating systems: linux, windows.
+__docker_daemon_os_is() {
+ local expected_os="$1"
+ local actual_os=${daemon_os=$(__docker_q version -f '{{.Server.Os}}')}
+ [ "$actual_os" = "$expected_os" ]
+}
+
+# __docker_pos_first_nonflag finds the position of the first word that is neither
+# option nor an option's argument. If there are options that require arguments,
+# you should pass a glob describing those options, e.g. "--option1|-o|--option2"
+# Use this function to restrict completions to exact positions after the argument list.
+__docker_pos_first_nonflag() {
+ local argument_flags=$1
+
+ local counter=$((${subcommand_pos:-${command_pos}} + 1))
+ while [ $counter -le $cword ]; do
+ if [ -n "$argument_flags" ] && eval "case '${words[$counter]}' in $argument_flags) true ;; *) false ;; esac"; then
+ (( counter++ ))
+ # eat "=" in case of --option=arg syntax
+ [ "${words[$counter]}" = "=" ] && (( counter++ ))
+ else
+ case "${words[$counter]}" in
+ -*)
+ ;;
+ *)
+ break
+ ;;
+ esac
+ fi
+
+ # Bash splits words at "=", retaining "=" as a word, examples:
+ # "--debug=false" => 3 words, "--log-opt syslog-facility=daemon" => 4 words
+ while [ "${words[$counter + 1]}" = "=" ] ; do
+ counter=$(( counter + 2))
+ done
+
+ (( counter++ ))
+ done
+
+ echo $counter
+}
+
+# __docker_map_key_of_current_option returns `key` if we are currently completing the
+# value of a map option (`key=value`) which matches the extglob given as an argument.
+# This function is needed for key-specific completions.
+__docker_map_key_of_current_option() {
+ local glob="$1"
+
+ local key glob_pos
+ if [ "$cur" = "=" ] ; then # key= case
+ key="$prev"
+ glob_pos=$((cword - 2))
+ elif [[ $cur == *=* ]] ; then # key=value case (OSX)
+ key=${cur%=*}
+ glob_pos=$((cword - 1))
+ elif [ "$prev" = "=" ] ; then
+ key=${words[$cword - 2]} # key=value case
+ glob_pos=$((cword - 3))
+ else
+ return
+ fi
+
+ [ "${words[$glob_pos]}" = "=" ] && ((glob_pos--)) # --option=key=value syntax
+
+ [[ ${words[$glob_pos]} == @($glob) ]] && echo "$key"
+}
+
+# __docker_value_of_option returns the value of the first option matching `option_glob`.
+# Valid values for `option_glob` are option names like `--log-level` and globs like
+# `--log-level|-l`
+# Only positions between the command and the current word are considered.
+__docker_value_of_option() {
+ local option_extglob=$(__docker_to_extglob "$1")
+
+ local counter=$((command_pos + 1))
+ while [ $counter -lt $cword ]; do
+ case ${words[$counter]} in
+ $option_extglob )
+ echo ${words[$counter + 1]}
+ break
+ ;;
+ esac
+ (( counter++ ))
+ done
+}
+
+# __docker_to_alternatives transforms a multiline list of strings into a single line
+# string with the words separated by `|`.
+# This is used to prepare arguments to __docker_pos_first_nonflag().
+__docker_to_alternatives() {
+ local parts=( $1 )
+ local IFS='|'
+ echo "${parts[*]}"
+}
+
+# __docker_to_extglob transforms a multiline list of options into an extglob pattern
+# suitable for use in case statements.
+__docker_to_extglob() {
+ local extglob=$( __docker_to_alternatives "$1" )
+ echo "@($extglob)"
+}
+
+# __docker_subcommands processes subcommands
+# Locates the first occurrence of any of the subcommands contained in the
+# first argument. In case of a match, calls the corresponding completion
+# function and returns 0.
+# If no match is found, 1 is returned. The calling function can then
+# continue processing its completion.
+#
+# TODO if the preceding command has options that accept arguments and an
+# argument is equal ot one of the subcommands, this is falsely detected as
+# a match.
+__docker_subcommands() {
+ local subcommands="$1"
+
+ local counter=$(($command_pos + 1))
+ while [ $counter -lt $cword ]; do
+ case "${words[$counter]}" in
+ $(__docker_to_extglob "$subcommands") )
+ subcommand_pos=$counter
+ local subcommand=${words[$counter]}
+ local completions_func=_docker_${command}_${subcommand//-/_}
+ declare -F $completions_func >/dev/null && $completions_func
+ return 0
+ ;;
+ esac
+ (( counter++ ))
+ done
+ return 1
+}
+
+# __docker_nospace suppresses trailing whitespace
+__docker_nospace() {
+ # compopt is not available in ancient bash versions
+ type compopt &>/dev/null && compopt -o nospace
+}
+
+__docker_complete_resolved_hostname() {
+ command -v host >/dev/null 2>&1 || return
+ COMPREPLY=( $(host 2>/dev/null "${cur%:}" | awk '/has address/ {print $4}') )
+}
+
+__docker_local_interfaces() {
+ command -v ip >/dev/null 2>&1 || return
+ ip addr show scope global 2>/dev/null | sed -n 's| \+inet \([0-9.]\+\).* \([^ ]\+\)|\1 \2|p'
+}
+
+__docker_complete_local_interfaces() {
+ local additional_interface
+ if [ "$1" = "--add" ] ; then
+ additional_interface="$2"
+ fi
+
+ COMPREPLY=( $( compgen -W "$(__docker_local_interfaces) $additional_interface" -- "$cur" ) )
+}
+
+# __docker_complete_capabilities_addable completes Linux capabilities which are
+# not granted by default and may be added.
+# see https://docs.docker.com/engine/reference/run/#/runtime-privilege-and-linux-capabilities
+__docker_complete_capabilities_addable() {
+ COMPREPLY=( $( compgen -W "
+ ALL
+ AUDIT_CONTROL
+ BLOCK_SUSPEND
+ DAC_READ_SEARCH
+ IPC_LOCK
+ IPC_OWNER
+ LEASE
+ LINUX_IMMUTABLE
+ MAC_ADMIN
+ MAC_OVERRIDE
+ NET_ADMIN
+ NET_BROADCAST
+ SYS_ADMIN
+ SYS_BOOT
+ SYSLOG
+ SYS_MODULE
+ SYS_NICE
+ SYS_PACCT
+ SYS_PTRACE
+ SYS_RAWIO
+ SYS_RESOURCE
+ SYS_TIME
+ SYS_TTY_CONFIG
+ WAKE_ALARM
+ " -- "$cur" ) )
+}
+
+# __docker_complete_capabilities_droppable completes Linux capability options which are
+# allowed by default and can be dropped.
+# see https://docs.docker.com/engine/reference/run/#/runtime-privilege-and-linux-capabilities
+__docker_complete_capabilities_droppable() {
+ COMPREPLY=( $( compgen -W "
+ ALL
+ AUDIT_WRITE
+ CHOWN
+ DAC_OVERRIDE
+ FOWNER
+ FSETID
+ KILL
+ MKNOD
+ NET_BIND_SERVICE
+ NET_RAW
+ SETFCAP
+ SETGID
+ SETPCAP
+ SETUID
+ SYS_CHROOT
+ " -- "$cur" ) )
+}
+
+__docker_complete_detach_keys() {
+ case "$prev" in
+ --detach-keys)
+ case "$cur" in
+ *,)
+ COMPREPLY=( $( compgen -W "${cur}ctrl-" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "ctrl-" -- "$cur" ) )
+ ;;
+ esac
+
+ __docker_nospace
+ return
+ ;;
+ esac
+ return 1
+}
+
+__docker_complete_isolation() {
+ COMPREPLY=( $( compgen -W "default hyperv process" -- "$cur" ) )
+}
+
+__docker_complete_log_drivers() {
+ COMPREPLY=( $( compgen -W "
+ awslogs
+ etwlogs
+ fluentd
+ gcplogs
+ gelf
+ journald
+ json-file
+ logentries
+ none
+ splunk
+ syslog
+ " -- "$cur" ) )
+}
+
+__docker_complete_log_options() {
+ # see repository docker/docker.github.io/engine/admin/logging/
+ local common_options="max-buffer-size mode"
+
+ local awslogs_options="$common_options awslogs-region awslogs-group awslogs-stream awslogs-create-group"
+ local fluentd_options="$common_options env fluentd-address fluentd-async-connect fluentd-buffer-limit fluentd-retry-wait fluentd-max-retries labels tag"
+ local gcplogs_options="$common_options env gcp-log-cmd gcp-project labels"
+ local gelf_options="$common_options env gelf-address gelf-compression-level gelf-compression-type labels tag"
+ local journald_options="$common_options env labels tag"
+ local json_file_options="$common_options env labels max-file max-size"
+ local logentries_options="$common_options logentries-token"
+ local syslog_options="$common_options env labels syslog-address syslog-facility syslog-format syslog-tls-ca-cert syslog-tls-cert syslog-tls-key syslog-tls-skip-verify tag"
+ local splunk_options="$common_options env labels splunk-caname splunk-capath splunk-format splunk-gzip splunk-gzip-level splunk-index splunk-insecureskipverify splunk-source splunk-sourcetype splunk-token splunk-url splunk-verify-connection tag"
+
+ local all_options="$fluentd_options $gcplogs_options $gelf_options $journald_options $logentries_options $json_file_options $syslog_options $splunk_options"
+
+ case $(__docker_value_of_option --log-driver) in
+ '')
+ COMPREPLY=( $( compgen -W "$all_options" -S = -- "$cur" ) )
+ ;;
+ awslogs)
+ COMPREPLY=( $( compgen -W "$awslogs_options" -S = -- "$cur" ) )
+ ;;
+ fluentd)
+ COMPREPLY=( $( compgen -W "$fluentd_options" -S = -- "$cur" ) )
+ ;;
+ gcplogs)
+ COMPREPLY=( $( compgen -W "$gcplogs_options" -S = -- "$cur" ) )
+ ;;
+ gelf)
+ COMPREPLY=( $( compgen -W "$gelf_options" -S = -- "$cur" ) )
+ ;;
+ journald)
+ COMPREPLY=( $( compgen -W "$journald_options" -S = -- "$cur" ) )
+ ;;
+ json-file)
+ COMPREPLY=( $( compgen -W "$json_file_options" -S = -- "$cur" ) )
+ ;;
+ logentries)
+ COMPREPLY=( $( compgen -W "$logentries_options" -S = -- "$cur" ) )
+ ;;
+ syslog)
+ COMPREPLY=( $( compgen -W "$syslog_options" -S = -- "$cur" ) )
+ ;;
+ splunk)
+ COMPREPLY=( $( compgen -W "$splunk_options" -S = -- "$cur" ) )
+ ;;
+ *)
+ return
+ ;;
+ esac
+
+ __docker_nospace
+}
+
+__docker_complete_log_driver_options() {
+ local key=$(__docker_map_key_of_current_option '--log-opt')
+ case "$key" in
+ fluentd-async-connect)
+ COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
+ return
+ ;;
+ gelf-address)
+ COMPREPLY=( $( compgen -W "udp" -S "://" -- "${cur##*=}" ) )
+ __docker_nospace
+ return
+ ;;
+ gelf-compression-level)
+ COMPREPLY=( $( compgen -W "1 2 3 4 5 6 7 8 9" -- "${cur##*=}" ) )
+ return
+ ;;
+ gelf-compression-type)
+ COMPREPLY=( $( compgen -W "gzip none zlib" -- "${cur##*=}" ) )
+ return
+ ;;
+ mode)
+ COMPREPLY=( $( compgen -W "blocking non-blocking" -- "${cur##*=}" ) )
+ return
+ ;;
+ syslog-address)
+ COMPREPLY=( $( compgen -W "tcp:// tcp+tls:// udp:// unix://" -- "${cur##*=}" ) )
+ __docker_nospace
+ __ltrim_colon_completions "${cur}"
+ return
+ ;;
+ syslog-facility)
+ COMPREPLY=( $( compgen -W "
+ auth
+ authpriv
+ cron
+ daemon
+ ftp
+ kern
+ local0
+ local1
+ local2
+ local3
+ local4
+ local5
+ local6
+ local7
+ lpr
+ mail
+ news
+ syslog
+ user
+ uucp
+ " -- "${cur##*=}" ) )
+ return
+ ;;
+ syslog-format)
+ COMPREPLY=( $( compgen -W "rfc3164 rfc5424 rfc5424micro" -- "${cur##*=}" ) )
+ return
+ ;;
+ syslog-tls-ca-cert|syslog-tls-cert|syslog-tls-key)
+ _filedir
+ return
+ ;;
+ syslog-tls-skip-verify)
+ COMPREPLY=( $( compgen -W "true" -- "${cur##*=}" ) )
+ return
+ ;;
+ splunk-url)
+ COMPREPLY=( $( compgen -W "http:// https://" -- "${cur##*=}" ) )
+ __docker_nospace
+ __ltrim_colon_completions "${cur}"
+ return
+ ;;
+ splunk-gzip|splunk-insecureskipverify|splunk-verify-connection)
+ COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
+ return
+ ;;
+ splunk-format)
+ COMPREPLY=( $( compgen -W "inline json raw" -- "${cur##*=}" ) )
+ return
+ ;;
+ esac
+ return 1
+}
+
+__docker_complete_log_levels() {
+ COMPREPLY=( $( compgen -W "debug info warn error fatal" -- "$cur" ) )
+}
+
+__docker_complete_restart() {
+ case "$prev" in
+ --restart)
+ case "$cur" in
+ on-failure:*)
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "always no on-failure on-failure: unless-stopped" -- "$cur") )
+ ;;
+ esac
+ return
+ ;;
+ esac
+ return 1
+}
+
+# __docker_complete_signals returns a subset of the available signals that is most likely
+# relevant in the context of docker containers
+__docker_complete_signals() {
+ local signals=(
+ SIGCONT
+ SIGHUP
+ SIGINT
+ SIGKILL
+ SIGQUIT
+ SIGSTOP
+ SIGTERM
+ SIGUSR1
+ SIGUSR2
+ )
+ COMPREPLY=( $( compgen -W "${signals[*]} ${signals[*]#SIG}" -- "$( echo $cur | tr '[:lower:]' '[:upper:]')" ) )
+}
+
+__docker_complete_user_group() {
+ if [[ $cur == *:* ]] ; then
+ COMPREPLY=( $(compgen -g -- "${cur#*:}") )
+ else
+ COMPREPLY=( $(compgen -u -S : -- "$cur") )
+ __docker_nospace
+ fi
+}
+
+_docker_docker() {
+ # global options that may appear after the docker command
+ local boolean_options="
+ $global_boolean_options
+ --help
+ --version -v
+ "
+
+ case "$prev" in
+ --config)
+ _filedir -d
+ return
+ ;;
+ --log-level|-l)
+ __docker_complete_log_levels
+ return
+ ;;
+ $(__docker_to_extglob "$global_options_with_args") )
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "$boolean_options $global_options_with_args" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$( __docker_pos_first_nonflag "$(__docker_to_extglob "$global_options_with_args")" )
+ if [ $cword -eq $counter ]; then
+ __docker_daemon_is_experimental && commands+=(${experimental_commands[*]})
+ COMPREPLY=( $( compgen -W "${commands[*]} help" -- "$cur" ) )
+ fi
+ ;;
+ esac
+}
+
+_docker_attach() {
+ _docker_container_attach
+}
+
+_docker_build() {
+ _docker_image_build
+}
+
+
+_docker_checkpoint() {
+ local subcommands="
+ create
+ ls
+ rm
+ "
+ local aliases="
+ list
+ remove
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_checkpoint_create() {
+ case "$prev" in
+ --checkpoint-dir)
+ _filedir -d
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--checkpoint-dir --help --leave-running" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--checkpoint-dir')
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_running
+ fi
+ ;;
+ esac
+}
+
+_docker_checkpoint_ls() {
+ case "$prev" in
+ --checkpoint-dir)
+ _filedir -d
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--checkpoint-dir --help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--checkpoint-dir')
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_all
+ fi
+ ;;
+ esac
+}
+
+_docker_checkpoint_rm() {
+ case "$prev" in
+ --checkpoint-dir)
+ _filedir -d
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--checkpoint-dir --help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--checkpoint-dir')
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_all
+ elif [ $cword -eq $(($counter + 1)) ]; then
+ COMPREPLY=( $( compgen -W "$(__docker_q checkpoint ls "$prev" | sed 1d)" -- "$cur" ) )
+ fi
+ ;;
+ esac
+}
+
+
+_docker_container() {
+ local subcommands="
+ attach
+ commit
+ cp
+ create
+ diff
+ exec
+ export
+ inspect
+ kill
+ logs
+ ls
+ pause
+ port
+ prune
+ rename
+ restart
+ rm
+ run
+ start
+ stats
+ stop
+ top
+ unpause
+ update
+ wait
+ "
+ local aliases="
+ list
+ ps
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_container_attach() {
+ __docker_complete_detach_keys && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--detach-keys --help --no-stdin --sig-proxy=false" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--detach-keys')
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_running
+ fi
+ ;;
+ esac
+}
+
+_docker_container_commit() {
+ case "$prev" in
+ --author|-a|--change|-c|--message|-m)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--author -a --change -c --help --message -m --pause=false -p=false" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--author|-a|--change|-c|--message|-m')
+
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_all
+ return
+ fi
+ (( counter++ ))
+
+ if [ $cword -eq $counter ]; then
+ __docker_complete_image_repos_and_tags
+ return
+ fi
+ ;;
+ esac
+}
+
+_docker_container_cp() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--follow-link -L --help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ case "$cur" in
+ *:)
+ return
+ ;;
+ *)
+ # combined container and filename completion
+ _filedir
+ local files=( ${COMPREPLY[@]} )
+
+ __docker_complete_containers_all
+ COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) )
+ local containers=( ${COMPREPLY[@]} )
+
+ COMPREPLY=( $( compgen -W "${files[*]} ${containers[*]}" -- "$cur" ) )
+ if [[ "$COMPREPLY" == *: ]]; then
+ __docker_nospace
+ fi
+ return
+ ;;
+ esac
+ fi
+ (( counter++ ))
+
+ if [ $cword -eq $counter ]; then
+ if [ -e "$prev" ]; then
+ __docker_complete_containers_all
+ COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) )
+ __docker_nospace
+ else
+ _filedir
+ fi
+ return
+ fi
+ ;;
+ esac
+}
+
+_docker_container_create() {
+ _docker_container_run_and_create
+}
+
+_docker_container_diff() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_all
+ fi
+ ;;
+ esac
+}
+
+_docker_container_exec() {
+ __docker_complete_detach_keys && return
+
+ case "$prev" in
+ --env|-e)
+ # we do not append a "=" here because "-e VARNAME" is legal systax, too
+ COMPREPLY=( $( compgen -e -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --user|-u)
+ __docker_complete_user_group
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--detach -d --detach-keys --env -e --help --interactive -i --privileged -t --tty -u --user" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_containers_running
+ ;;
+ esac
+}
+
+_docker_container_export() {
+ case "$prev" in
+ --output|-o)
+ _filedir
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --output -o" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_all
+ fi
+ ;;
+ esac
+}
+
+_docker_container_inspect() {
+ _docker_inspect --type container
+}
+
+_docker_container_kill() {
+ case "$prev" in
+ --signal|-s)
+ __docker_complete_signals
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --signal -s" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_containers_running
+ ;;
+ esac
+}
+
+_docker_container_logs() {
+ case "$prev" in
+ --since|--tail)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--details --follow -f --help --since --tail --timestamps -t" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--since|--tail')
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_all
+ fi
+ ;;
+ esac
+}
+
+_docker_container_list() {
+ _docker_container_ls
+}
+
+_docker_container_ls() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ ancestor)
+ cur="${cur##*=}"
+ __docker_complete_images
+ return
+ ;;
+ before)
+ __docker_complete_containers_all --cur "${cur##*=}"
+ return
+ ;;
+ expose|publish)
+ return
+ ;;
+ id)
+ __docker_complete_containers_all --cur "${cur##*=}" --id
+ return
+ ;;
+ health)
+ COMPREPLY=( $( compgen -W "healthy starting none unhealthy" -- "${cur##*=}" ) )
+ return
+ ;;
+ is-task)
+ COMPREPLY=( $( compgen -W "true false" -- "${cur##*=}" ) )
+ return
+ ;;
+ name)
+ __docker_complete_containers_all --cur "${cur##*=}" --name
+ return
+ ;;
+ network)
+ __docker_complete_networks --cur "${cur##*=}"
+ return
+ ;;
+ since)
+ __docker_complete_containers_all --cur "${cur##*=}"
+ return
+ ;;
+ status)
+ COMPREPLY=( $( compgen -W "created dead exited paused restarting running removing" -- "${cur##*=}" ) )
+ return
+ ;;
+ volume)
+ __docker_complete_volumes --cur "${cur##*=}"
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -S = -W "ancestor before exited expose health id is-task label name network publish since status volume" -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --format|--last|-n)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--all -a --filter -f --format --help --last -n --latest -l --no-trunc --quiet -q --size -s" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_container_pause() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_containers_running
+ ;;
+ esac
+}
+
+_docker_container_port() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_all
+ fi
+ ;;
+ esac
+}
+
+_docker_container_prune() {
+ case "$prev" in
+ --filter)
+ COMPREPLY=( $( compgen -W "until" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--force -f --filter --help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_container_ps() {
+ _docker_container_ls
+}
+
+_docker_container_rename() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_all
+ fi
+ ;;
+ esac
+}
+
+_docker_container_restart() {
+ case "$prev" in
+ --time|-t)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --time -t" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_containers_all
+ ;;
+ esac
+}
+
+_docker_container_rm() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--force -f --help --link -l --volumes -v" -- "$cur" ) )
+ ;;
+ *)
+ for arg in "${COMP_WORDS[@]}"; do
+ case "$arg" in
+ --force|-f)
+ __docker_complete_containers_all
+ return
+ ;;
+ esac
+ done
+ __docker_complete_containers_stopped
+ ;;
+ esac
+}
+
+_docker_container_run() {
+ _docker_container_run_and_create
+}
+
+# _docker_container_run_and_create is the combined completion for `_docker_container_run`
+# and `_docker_container_create`
+_docker_container_run_and_create() {
+ local options_with_args="
+ --add-host
+ --attach -a
+ --blkio-weight
+ --blkio-weight-device
+ --cap-add
+ --cap-drop
+ --cgroup-parent
+ --cidfile
+ --cpu-period
+ --cpu-quota
+ --cpu-rt-period
+ --cpu-rt-runtime
+ --cpuset-cpus
+ --cpus
+ --cpuset-mems
+ --cpu-shares -c
+ --device
+ --device-cgroup-rule
+ --device-read-bps
+ --device-read-iops
+ --device-write-bps
+ --device-write-iops
+ --dns
+ --dns-option
+ --dns-search
+ --entrypoint
+ --env -e
+ --env-file
+ --expose
+ --group-add
+ --hostname -h
+ --init-path
+ --ip
+ --ip6
+ --ipc
+ --kernel-memory
+ --label-file
+ --label -l
+ --link
+ --link-local-ip
+ --log-driver
+ --log-opt
+ --mac-address
+ --memory -m
+ --memory-swap
+ --memory-swappiness
+ --memory-reservation
+ --name
+ --network
+ --network-alias
+ --oom-score-adj
+ --pid
+ --pids-limit
+ --publish -p
+ --restart
+ --runtime
+ --security-opt
+ --shm-size
+ --stop-signal
+ --stop-timeout
+ --storage-opt
+ --tmpfs
+ --sysctl
+ --ulimit
+ --user -u
+ --userns
+ --uts
+ --volume-driver
+ --volumes-from
+ --volume -v
+ --workdir -w
+ "
+ __docker_daemon_os_is windows && options_with_args+="
+ --cpu-count
+ --cpu-percent
+ --credentialspec
+ --io-maxbandwidth
+ --io-maxiops
+ --isolation
+ "
+
+ local boolean_options="
+ --disable-content-trust=false
+ --help
+ --init
+ --interactive -i
+ --oom-kill-disable
+ --privileged
+ --publish-all -P
+ --read-only
+ --tty -t
+ "
+
+ if [ "$command" = "run" -o "$subcommand" = "run" ] ; then
+ options_with_args="$options_with_args
+ --detach-keys
+ --health-cmd
+ --health-interval
+ --health-retries
+ --health-timeout
+ "
+ boolean_options="$boolean_options
+ --detach -d
+ --no-healthcheck
+ --rm
+ --sig-proxy=false
+ "
+ __docker_complete_detach_keys && return
+ fi
+
+ local all_options="$options_with_args $boolean_options"
+
+
+ __docker_complete_log_driver_options && return
+ __docker_complete_restart && return
+
+ local key=$(__docker_map_key_of_current_option '--security-opt')
+ case "$key" in
+ label)
+ [[ $cur == *: ]] && return
+ COMPREPLY=( $( compgen -W "user: role: type: level: disable" -- "${cur##*=}") )
+ if [ "${COMPREPLY[*]}" != "disable" ] ; then
+ __docker_nospace
+ fi
+ return
+ ;;
+ seccomp)
+ local cur=${cur##*=}
+ _filedir
+ COMPREPLY+=( $( compgen -W "unconfined" -- "$cur" ) )
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --add-host)
+ case "$cur" in
+ *:)
+ __docker_complete_resolved_hostname
+ return
+ ;;
+ esac
+ ;;
+ --attach|-a)
+ COMPREPLY=( $( compgen -W 'stdin stdout stderr' -- "$cur" ) )
+ return
+ ;;
+ --cap-add)
+ __docker_complete_capabilities_addable
+ return
+ ;;
+ --cap-drop)
+ __docker_complete_capabilities_droppable
+ return
+ ;;
+ --cidfile|--env-file|--init-path|--label-file)
+ _filedir
+ return
+ ;;
+ --device|--tmpfs|--volume|-v)
+ case "$cur" in
+ *:*)
+ # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
+ ;;
+ '')
+ COMPREPLY=( $( compgen -W '/' -- "$cur" ) )
+ __docker_nospace
+ ;;
+ /*)
+ _filedir
+ __docker_nospace
+ ;;
+ esac
+ return
+ ;;
+ --env|-e)
+ # we do not append a "=" here because "-e VARNAME" is legal systax, too
+ COMPREPLY=( $( compgen -e -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --ipc)
+ case "$cur" in
+ *:*)
+ cur="${cur#*:}"
+ __docker_complete_containers_running
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W 'host container:' -- "$cur" ) )
+ if [ "$COMPREPLY" = "container:" ]; then
+ __docker_nospace
+ fi
+ ;;
+ esac
+ return
+ ;;
+ --isolation)
+ if __docker_daemon_os_is windows ; then
+ __docker_complete_isolation
+ return
+ fi
+ ;;
+ --link)
+ case "$cur" in
+ *:*)
+ ;;
+ *)
+ __docker_complete_containers_running
+ COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) )
+ __docker_nospace
+ ;;
+ esac
+ return
+ ;;
+ --log-driver)
+ __docker_complete_log_drivers
+ return
+ ;;
+ --log-opt)
+ __docker_complete_log_options
+ return
+ ;;
+ --network)
+ case "$cur" in
+ container:*)
+ __docker_complete_containers_all --cur "${cur#*:}"
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$(__docker_plugins_bundled --type Network) $(__docker_networks) container:" -- "$cur") )
+ if [ "${COMPREPLY[*]}" = "container:" ] ; then
+ __docker_nospace
+ fi
+ ;;
+ esac
+ return
+ ;;
+ --pid)
+ case "$cur" in
+ *:*)
+ __docker_complete_containers_running --cur "${cur#*:}"
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W 'host container:' -- "$cur" ) )
+ if [ "$COMPREPLY" = "container:" ]; then
+ __docker_nospace
+ fi
+ ;;
+ esac
+ return
+ ;;
+ --runtime)
+ __docker_complete_runtimes
+ return
+ ;;
+ --security-opt)
+ COMPREPLY=( $( compgen -W "apparmor= label= no-new-privileges seccomp=" -- "$cur") )
+ if [ "${COMPREPLY[*]}" != "no-new-privileges" ] ; then
+ __docker_nospace
+ fi
+ return
+ ;;
+ --stop-signal)
+ __docker_complete_signals
+ return
+ ;;
+ --storage-opt)
+ COMPREPLY=( $( compgen -W "size" -S = -- "$cur") )
+ __docker_nospace
+ return
+ ;;
+ --user|-u)
+ __docker_complete_user_group
+ return
+ ;;
+ --userns)
+ COMPREPLY=( $( compgen -W "host" -- "$cur" ) )
+ return
+ ;;
+ --volume-driver)
+ __docker_complete_plugins_bundled --type Volume
+ return
+ ;;
+ --volumes-from)
+ __docker_complete_containers_all
+ return
+ ;;
+ $(__docker_to_extglob "$options_with_args") )
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "$all_options" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
+ if [ $cword -eq $counter ]; then
+ __docker_complete_images
+ fi
+ ;;
+ esac
+}
+
+_docker_container_start() {
+ __docker_complete_detach_keys && return
+
+ case "$prev" in
+ --checkpoint)
+ if [ __docker_daemon_is_experimental ] ; then
+ return
+ fi
+ ;;
+ --checkpoint-dir)
+ if [ __docker_daemon_is_experimental ] ; then
+ _filedir -d
+ return
+ fi
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ local options="--attach -a --detach-keys --help --interactive -i"
+ __docker_daemon_is_experimental && options+=" --checkpoint --checkpoint-dir"
+ COMPREPLY=( $( compgen -W "$options" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_containers_stopped
+ ;;
+ esac
+}
+
+_docker_container_stats() {
+ case "$prev" in
+ --format)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--all -a --format --help --no-stream" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_containers_running
+ ;;
+ esac
+}
+
+_docker_container_stop() {
+ case "$prev" in
+ --time|-t)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --time -t" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_containers_running
+ ;;
+ esac
+}
+
+_docker_container_top() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_running
+ fi
+ ;;
+ esac
+}
+
+_docker_container_unpause() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_containers_unpauseable
+ fi
+ ;;
+ esac
+}
+
+_docker_container_update() {
+ local options_with_args="
+ --blkio-weight
+ --cpu-period
+ --cpu-quota
+ --cpu-rt-period
+ --cpu-rt-runtime
+ --cpuset-cpus
+ --cpuset-mems
+ --cpu-shares -c
+ --kernel-memory
+ --memory -m
+ --memory-reservation
+ --memory-swap
+ --restart
+ "
+
+ local boolean_options="
+ --help
+ "
+
+ local all_options="$options_with_args $boolean_options"
+
+ __docker_complete_restart && return
+
+ case "$prev" in
+ $(__docker_to_extglob "$options_with_args") )
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "$all_options" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_containers_all
+ ;;
+ esac
+}
+
+_docker_container_wait() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_containers_all
+ ;;
+ esac
+}
+
+
+_docker_commit() {
+ _docker_container_commit
+}
+
+_docker_cp() {
+ _docker_container_cp
+}
+
+_docker_create() {
+ _docker_container_create
+}
+
+_docker_daemon() {
+ local boolean_options="
+ $global_boolean_options
+ --disable-legacy-registry
+ --experimental
+ --help
+ --icc=false
+ --init
+ --ip-forward=false
+ --ip-masq=false
+ --iptables=false
+ --ipv6
+ --live-restore
+ --raw-logs
+ --selinux-enabled
+ --userland-proxy=false
+ "
+ local options_with_args="
+ $global_options_with_args
+ --add-runtime
+ --api-cors-header
+ --authorization-plugin
+ --bip
+ --bridge -b
+ --cgroup-parent
+ --cluster-advertise
+ --cluster-store
+ --cluster-store-opt
+ --config-file
+ --containerd
+ --default-gateway
+ --default-gateway-v6
+ --default-shm-size
+ --default-ulimit
+ --dns
+ --dns-search
+ --dns-opt
+ --exec-opt
+ --exec-root
+ --fixed-cidr
+ --fixed-cidr-v6
+ --graph -g
+ --group -G
+ --init-path
+ --insecure-registry
+ --ip
+ --label
+ --log-driver
+ --log-opt
+ --max-concurrent-downloads
+ --max-concurrent-uploads
+ --mtu
+ --oom-score-adjust
+ --pidfile -p
+ --registry-mirror
+ --seccomp-profile
+ --shutdown-timeout
+ --storage-driver -s
+ --storage-opt
+ --userland-proxy-path
+ --userns-remap
+ "
+
+ __docker_complete_log_driver_options && return
+
+ key=$(__docker_map_key_of_current_option '--cluster-store-opt')
+ case "$key" in
+ kv.*file)
+ cur=${cur##*=}
+ _filedir
+ return
+ ;;
+ esac
+
+ local key=$(__docker_map_key_of_current_option '--storage-opt')
+ case "$key" in
+ dm.blkdiscard|dm.override_udev_sync_check|dm.use_deferred_removal|dm.use_deferred_deletion)
+ COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
+ return
+ ;;
+ dm.fs)
+ COMPREPLY=( $( compgen -W "ext4 xfs" -- "${cur##*=}" ) )
+ return
+ ;;
+ dm.thinpooldev)
+ cur=${cur##*=}
+ _filedir
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --authorization-plugin)
+ __docker_complete_plugins_bundled --type Authorization
+ return
+ ;;
+ --cluster-store)
+ COMPREPLY=( $( compgen -W "consul etcd zk" -S "://" -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --cluster-store-opt)
+ COMPREPLY=( $( compgen -W "discovery.heartbeat discovery.ttl kv.cacertfile kv.certfile kv.keyfile kv.path" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --config-file|--containerd|--init-path|--pidfile|-p|--tlscacert|--tlscert|--tlskey|--userland-proxy-path)
+ _filedir
+ return
+ ;;
+ --exec-root|--graph|-g)
+ _filedir -d
+ return
+ ;;
+ --log-driver)
+ __docker_complete_log_drivers
+ return
+ ;;
+ --storage-driver|-s)
+ COMPREPLY=( $( compgen -W "aufs btrfs devicemapper overlay overlay2 vfs zfs" -- "$(echo $cur | tr '[:upper:]' '[:lower:]')" ) )
+ return
+ ;;
+ --storage-opt)
+ local btrfs_options="btrfs.min_space"
+ local devicemapper_options="
+ dm.basesize
+ dm.blkdiscard
+ dm.blocksize
+ dm.fs
+ dm.loopdatasize
+ dm.loopmetadatasize
+ dm.min_free_space
+ dm.mkfsarg
+ dm.mountopt
+ dm.override_udev_sync_check
+ dm.thinpooldev
+ dm.use_deferred_deletion
+ dm.use_deferred_removal
+ "
+ local zfs_options="zfs.fsname"
+
+ case $(__docker_value_of_option '--storage-driver|-s') in
+ '')
+ COMPREPLY=( $( compgen -W "$btrfs_options $devicemapper_options $zfs_options" -S = -- "$cur" ) )
+ ;;
+ btrfs)
+ COMPREPLY=( $( compgen -W "$btrfs_options" -S = -- "$cur" ) )
+ ;;
+ devicemapper)
+ COMPREPLY=( $( compgen -W "$devicemapper_options" -S = -- "$cur" ) )
+ ;;
+ zfs)
+ COMPREPLY=( $( compgen -W "$zfs_options" -S = -- "$cur" ) )
+ ;;
+ *)
+ return
+ ;;
+ esac
+ __docker_nospace
+ return
+ ;;
+ --log-level|-l)
+ __docker_complete_log_levels
+ return
+ ;;
+ --log-opt)
+ __docker_complete_log_options
+ return
+ ;;
+ --seccomp-profile)
+ _filedir json
+ return
+ ;;
+ --userns-remap)
+ __docker_complete_user_group
+ return
+ ;;
+ $(__docker_to_extglob "$options_with_args") )
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_deploy() {
+ __docker_daemon_is_experimental && _docker_stack_deploy
+}
+
+_docker_diff() {
+ _docker_container_diff
+}
+
+_docker_events() {
+ _docker_system_events
+}
+
+_docker_exec() {
+ _docker_container_exec
+}
+
+_docker_export() {
+ _docker_container_export
+}
+
+_docker_help() {
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ COMPREPLY=( $( compgen -W "${commands[*]}" -- "$cur" ) )
+ fi
+}
+
+_docker_history() {
+ _docker_image_history
+}
+
+
+_docker_image() {
+ local subcommands="
+ build
+ history
+ import
+ inspect
+ load
+ ls
+ prune
+ pull
+ push
+ rm
+ save
+ tag
+ "
+ local aliases="
+ images
+ list
+ remove
+ rmi
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_image_build() {
+ local options_with_args="
+ --add-host
+ --build-arg
+ --cache-from
+ --cgroup-parent
+ --cpuset-cpus
+ --cpuset-mems
+ --cpu-shares -c
+ --cpu-period
+ --cpu-quota
+ --file -f
+ --label
+ --memory -m
+ --memory-swap
+ --network
+ --shm-size
+ --tag -t
+ --ulimit
+ "
+ __docker_daemon_os_is windows && options_with_args+="
+ --isolation
+ "
+
+ local boolean_options="
+ --compress
+ --disable-content-trust=false
+ --force-rm
+ --help
+ --no-cache
+ --pull
+ --quiet -q
+ --rm
+ "
+ __docker_daemon_is_experimental && boolean_options+="--squash"
+
+ local all_options="$options_with_args $boolean_options"
+
+ case "$prev" in
+ --add-host)
+ case "$cur" in
+ *:)
+ __docker_complete_resolved_hostname
+ return
+ ;;
+ esac
+ ;;
+ --build-arg)
+ COMPREPLY=( $( compgen -e -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --cache-from)
+ __docker_complete_image_repos_and_tags
+ return
+ ;;
+ --file|-f)
+ _filedir
+ return
+ ;;
+ --isolation)
+ if __docker_daemon_os_is windows ; then
+ __docker_complete_isolation
+ return
+ fi
+ ;;
+ --network)
+ case "$cur" in
+ container:*)
+ __docker_complete_containers_all --cur "${cur#*:}"
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$(__docker_plugins --type Network) $(__docker_networks) container:" -- "$cur") )
+ if [ "${COMPREPLY[*]}" = "container:" ] ; then
+ __docker_nospace
+ fi
+ ;;
+ esac
+ return
+ ;;
+ --tag|-t)
+ __docker_complete_image_repos_and_tags
+ return
+ ;;
+ $(__docker_to_extglob "$options_with_args") )
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "$all_options" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
+ if [ $cword -eq $counter ]; then
+ _filedir -d
+ fi
+ ;;
+ esac
+}
+
+_docker_image_history() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --human=false -H=false --no-trunc --quiet -q" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_images
+ fi
+ ;;
+ esac
+}
+
+_docker_image_images() {
+ _docker_image_ls
+}
+
+_docker_image_import() {
+ case "$prev" in
+ --change|-c|--message|-m)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--change -c --help --message -m" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--change|-c|--message|-m')
+ if [ $cword -eq $counter ]; then
+ return
+ fi
+ (( counter++ ))
+
+ if [ $cword -eq $counter ]; then
+ __docker_complete_image_repos_and_tags
+ return
+ fi
+ ;;
+ esac
+}
+
+_docker_image_inspect() {
+ _docker_inspect --type image
+}
+
+_docker_image_load() {
+ case "$prev" in
+ --input|-i)
+ _filedir
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --input -i --quiet -q" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_image_list() {
+ _docker_image_ls
+}
+
+_docker_image_ls() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ before|since|reference)
+ cur="${cur##*=}"
+ __docker_complete_images
+ return
+ ;;
+ dangling)
+ COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
+ return
+ ;;
+ label)
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -S = -W "before dangling label reference since" -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --format)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--all -a --digests --filter -f --format --help --no-trunc --quiet -q" -- "$cur" ) )
+ ;;
+ =)
+ return
+ ;;
+ *)
+ __docker_complete_image_repos
+ ;;
+ esac
+}
+
+_docker_image_prune() {
+ case "$prev" in
+ --filter)
+ COMPREPLY=( $( compgen -W "until" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--all -a --force -f --filter --help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_image_pull() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--all-tags -a --disable-content-trust=false --help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ for arg in "${COMP_WORDS[@]}"; do
+ case "$arg" in
+ --all-tags|-a)
+ __docker_complete_image_repos
+ return
+ ;;
+ esac
+ done
+ __docker_complete_image_repos_and_tags
+ fi
+ ;;
+ esac
+}
+
+_docker_image_push() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--disable-content-trust=false --help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_image_repos_and_tags
+ fi
+ ;;
+ esac
+}
+
+_docker_image_remove() {
+ _docker_image_rm
+}
+
+_docker_image_rm() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--force -f --help --no-prune" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_images
+ ;;
+ esac
+}
+
+_docker_image_rmi() {
+ _docker_image_rm
+}
+
+_docker_image_save() {
+ case "$prev" in
+ --output|-o)
+ _filedir
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --output -o" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_images
+ ;;
+ esac
+}
+
+_docker_image_tag() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+
+ if [ $cword -eq $counter ]; then
+ __docker_complete_image_repos_and_tags
+ return
+ fi
+ (( counter++ ))
+
+ if [ $cword -eq $counter ]; then
+ __docker_complete_image_repos_and_tags
+ return
+ fi
+ ;;
+ esac
+}
+
+
+_docker_images() {
+ _docker_image_ls
+}
+
+_docker_import() {
+ _docker_image_import
+}
+
+_docker_info() {
+ _docker_system_info
+}
+
+_docker_inspect() {
+ local preselected_type
+ local type
+
+ if [ "$1" = "--type" ] ; then
+ preselected_type=yes
+ type="$2"
+ else
+ type=$(__docker_value_of_option --type)
+ fi
+
+ case "$prev" in
+ --format|-f)
+ return
+ ;;
+ --type)
+ if [ -z "$preselected_type" ] ; then
+ COMPREPLY=( $( compgen -W "container image network node plugin service volume" -- "$cur" ) )
+ return
+ fi
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ local options="--format -f --help --size -s"
+ if [ -z "$preselected_type" ] ; then
+ options+=" --type"
+ fi
+ COMPREPLY=( $( compgen -W "$options" -- "$cur" ) )
+ ;;
+ *)
+ case "$type" in
+ '')
+ COMPREPLY=( $( compgen -W "
+ $(__docker_containers --all)
+ $(__docker_images)
+ $(__docker_networks)
+ $(__docker_nodes)
+ $(__docker_plugins_installed)
+ $(__docker_services)
+ $(__docker_volumes)
+ " -- "$cur" ) )
+ ;;
+ container)
+ __docker_complete_containers_all
+ ;;
+ image)
+ __docker_complete_images
+ ;;
+ network)
+ __docker_complete_networks
+ ;;
+ node)
+ __docker_complete_nodes
+ ;;
+ plugin)
+ __docker_complete_plugins_installed
+ ;;
+ service)
+ __docker_complete_services
+ ;;
+ volume)
+ __docker_complete_volumes
+ ;;
+ esac
+ esac
+}
+
+_docker_kill() {
+ _docker_container_kill
+}
+
+_docker_load() {
+ _docker_image_load
+}
+
+_docker_login() {
+ case "$prev" in
+ --password|-p|--username|-u)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --password -p --username -u" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_logout() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_logs() {
+ _docker_container_logs
+}
+
+_docker_network_connect() {
+ local options_with_args="
+ --alias
+ --ip
+ --ip6
+ --link
+ --link-local-ip
+ "
+
+ local boolean_options="
+ --help
+ "
+
+ case "$prev" in
+ --link)
+ case "$cur" in
+ *:*)
+ ;;
+ *)
+ __docker_complete_containers_running
+ COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) )
+ __docker_nospace
+ ;;
+ esac
+ return
+ ;;
+ $(__docker_to_extglob "$options_with_args") )
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
+ if [ $cword -eq $counter ]; then
+ __docker_complete_networks
+ elif [ $cword -eq $(($counter + 1)) ]; then
+ __docker_complete_containers_all
+ fi
+ ;;
+ esac
+}
+
+_docker_network_create() {
+ case "$prev" in
+ --aux-address|--gateway|--internal|--ip-range|--ipam-opt|--ipv6|--opt|-o|--subnet)
+ return
+ ;;
+ --ipam-driver)
+ COMPREPLY=( $( compgen -W "default" -- "$cur" ) )
+ return
+ ;;
+ --driver|-d)
+ # remove drivers that allow one instance only, add drivers missing in `docker info`
+ __docker_complete_plugins_bundled --type Network --remove host --remove null --add macvlan
+ return
+ ;;
+ --label)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--attachable --aux-address --driver -d --gateway --help --internal --ip-range --ipam-driver --ipam-opt --ipv6 --label --opt -o --subnet" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_network_disconnect() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_networks
+ elif [ $cword -eq $(($counter + 1)) ]; then
+ __docker_complete_containers_in_network "$prev"
+ fi
+ ;;
+ esac
+}
+
+_docker_network_inspect() {
+ case "$prev" in
+ --format|-f)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_networks
+ esac
+}
+
+_docker_network_ls() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ driver)
+ __docker_complete_plugins_bundled --cur "${cur##*=}" --type Network --add macvlan
+ return
+ ;;
+ id)
+ __docker_complete_networks --cur "${cur##*=}" --id
+ return
+ ;;
+ name)
+ __docker_complete_networks --cur "${cur##*=}" --name
+ return
+ ;;
+ type)
+ COMPREPLY=( $( compgen -W "builtin custom" -- "${cur##*=}" ) )
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -S = -W "driver id label name type" -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --format)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--filter -f --format --help --no-trunc --quiet -q" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_network_prune() {
+ case "$prev" in
+ --filter)
+ COMPREPLY=( $( compgen -W "until" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--force -f --filter --help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_network_rm() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_networks --filter type=custom
+ esac
+}
+
+_docker_network() {
+ local subcommands="
+ connect
+ create
+ disconnect
+ inspect
+ ls
+ prune
+ rm
+ "
+ local aliases="
+ list
+ remove
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_service() {
+ local subcommands="
+ create
+ inspect
+ ls
+ rm
+ scale
+ ps
+ update
+ "
+ __docker_daemon_is_experimental && subcommands+="logs"
+
+ local aliases="
+ list
+ remove
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_service_create() {
+ _docker_service_update_and_create
+}
+
+_docker_service_inspect() {
+ case "$prev" in
+ --format|-f)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--format -f --help --pretty" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_services
+ esac
+}
+
+_docker_service_logs() {
+ case "$prev" in
+ --since|--tail)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--follow -f --help --no-resolve --no-task-ids --no-trunc --since --tail --timestamps -t" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--since|--tail')
+ if [ $cword -eq $counter ]; then
+ __docker_complete_services
+ fi
+ ;;
+ esac
+}
+
+_docker_service_list() {
+ _docker_service_ls
+}
+
+_docker_service_ls() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ id)
+ __docker_complete_services --cur "${cur##*=}" --id
+ return
+ ;;
+ name)
+ __docker_complete_services --cur "${cur##*=}" --name
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -W "id label name" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --format)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--filter -f --format --help --quiet -q" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_service_remove() {
+ _docker_service_rm
+}
+
+_docker_service_rm() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_services
+ esac
+}
+
+_docker_service_scale() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_services
+ __docker_append_to_completions "="
+ __docker_nospace
+ ;;
+ esac
+}
+
+_docker_service_ps() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ desired-state)
+ COMPREPLY=( $( compgen -W "accepted running shutdown" -- "${cur##*=}" ) )
+ return
+ ;;
+ name)
+ __docker_complete_services --cur "${cur##*=}" --name
+ return
+ ;;
+ node)
+ __docker_complete_nodes --cur "${cur##*=}" --add self
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -W "desired-state id name node" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--filter -f --help --no-resolve --no-trunc --quiet -q" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--filter|-f')
+ if [ $cword -eq $counter ]; then
+ __docker_complete_services
+ fi
+ ;;
+ esac
+}
+
+_docker_service_update() {
+ _docker_service_update_and_create
+}
+
+# _docker_service_update_and_create is the combined completion for `docker service create`
+# and `docker service update`
+_docker_service_update_and_create() {
+ local $subcommand="${words[$subcommand_pos]}"
+
+ local options_with_args="
+ --endpoint-mode
+ --env -e
+ --force
+ --health-cmd
+ --health-interval
+ --health-retries
+ --health-timeout
+ --hostname
+ --label -l
+ --limit-cpu
+ --limit-memory
+ --log-driver
+ --log-opt
+ --mount
+ --network
+ --no-healthcheck
+ --replicas
+ --reserve-cpu
+ --reserve-memory
+ --restart-condition
+ --restart-delay
+ --restart-max-attempts
+ --restart-window
+ --rollback-delay
+ --rollback-failure-action
+ --rollback-max-failure-ratio
+ --rollback-monitor
+ --rollback-parallelism
+ --stop-grace-period
+ --stop-signal
+ --update-delay
+ --update-failure-action
+ --update-max-failure-ratio
+ --update-monitor
+ --update-parallelism
+ --user -u
+ --workdir -w
+ "
+
+ local boolean_options="
+ --help
+ --read-only
+ --tty -t
+ --with-registry-auth
+ "
+
+ __docker_complete_log_driver_options && return
+
+ if [ "$subcommand" = "create" ] ; then
+ options_with_args="$options_with_args
+ --constraint
+ --container-label
+ --dns
+ --dns-option
+ --dns-search
+ --env-file
+ --group
+ --host
+ --mode
+ --name
+ --placement-pref
+ --publish -p
+ --secret
+ "
+
+ case "$prev" in
+ --env-file)
+ _filedir
+ return
+ ;;
+ --group)
+ COMPREPLY=( $(compgen -g -- "$cur") )
+ return
+ ;;
+ --host)
+ case "$cur" in
+ *:)
+ __docker_complete_resolved_hostname
+ return
+ ;;
+ esac
+ ;;
+ --mode)
+ COMPREPLY=( $( compgen -W "global replicated" -- "$cur" ) )
+ return
+ ;;
+ --placement-pref)
+ COMPREPLY=( $( compgen -W "spread" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --secret)
+ __docker_complete_secrets
+ return
+ ;;
+ esac
+ fi
+ if [ "$subcommand" = "update" ] ; then
+ options_with_args="$options_with_args
+ --arg
+ --constraint-add
+ --constraint-rm
+ --container-label-add
+ --container-label-rm
+ --dns-add
+ --dns-option-add
+ --dns-option-rm
+ --dns-rm
+ --dns-search-add
+ --dns-search-rm
+ --group-add
+ --group-rm
+ --host-add
+ --host-rm
+ --image
+ --placement-pref-add
+ --placement-pref-rm
+ --publish-add
+ --publish-rm
+ --rollback
+ --secret-add
+ --secret-rm
+ "
+
+ case "$prev" in
+ --group-add|--group-rm)
+ COMPREPLY=( $(compgen -g -- "$cur") )
+ return
+ ;;
+ --host-add|--host-rm)
+ case "$cur" in
+ *:)
+ __docker_complete_resolved_hostname
+ return
+ ;;
+ esac
+ ;;
+ --image)
+ __docker_complete_image_repos_and_tags
+ return
+ ;;
+ --placement-pref-add|--placement-pref-rm)
+ COMPREPLY=( $( compgen -W "spread" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --secret-add|--secret-rm)
+ __docker_complete_secrets
+ return
+ ;;
+ esac
+ fi
+
+ local strategy=$(__docker_map_key_of_current_option '--placement-pref|--placement-pref-add|--placement-pref-rm')
+ case "$strategy" in
+ spread)
+ COMPREPLY=( $( compgen -W "engine.labels node.labels" -S . -- "${cur##*=}" ) )
+ __docker_nospace
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --endpoint-mode)
+ COMPREPLY=( $( compgen -W "dnsrr vip" -- "$cur" ) )
+ return
+ ;;
+ --env|-e)
+ # we do not append a "=" here because "-e VARNAME" is legal systax, too
+ COMPREPLY=( $( compgen -e -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --log-driver)
+ __docker_complete_log_drivers
+ return
+ ;;
+ --log-opt)
+ __docker_complete_log_options
+ return
+ ;;
+ --network)
+ __docker_complete_networks
+ return
+ ;;
+ --restart-condition)
+ COMPREPLY=( $( compgen -W "any none on-failure" -- "$cur" ) )
+ return
+ ;;
+ --rollback-failure-action)
+ COMPREPLY=( $( compgen -W "continue pause" -- "$cur" ) )
+ return
+ ;;
+ --stop-signal)
+ __docker_complete_signals
+ return
+ ;;
+ --update-failure-action)
+ COMPREPLY=( $( compgen -W "continue pause rollback" -- "$cur" ) )
+ return
+ ;;
+ --user|-u)
+ __docker_complete_user_group
+ return
+ ;;
+ $(__docker_to_extglob "$options_with_args") )
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "$boolean_options $options_with_args" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$( __docker_pos_first_nonflag $( __docker_to_alternatives "$options_with_args" ) )
+ if [ "$subcommand" = "update" ] ; then
+ if [ $cword -eq $counter ]; then
+ __docker_complete_services
+ fi
+ else
+ if [ $cword -eq $counter ]; then
+ __docker_complete_images
+ fi
+ fi
+ ;;
+ esac
+}
+
+_docker_swarm() {
+ local subcommands="
+ init
+ join
+ join-token
+ leave
+ unlock
+ unlock-key
+ update
+ "
+ __docker_subcommands "$subcommands" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_swarm_init() {
+ case "$prev" in
+ --advertise-addr)
+ if [[ $cur == *: ]] ; then
+ COMPREPLY=( $( compgen -W "2377" -- "${cur##*:}" ) )
+ else
+ __docker_complete_local_interfaces
+ __docker_nospace
+ fi
+ return
+ ;;
+ --availability)
+ COMPREPLY=( $( compgen -W "active drain pause" -- "$cur" ) )
+ return
+ ;;
+ --cert-expiry|--dispatcher-heartbeat|--external-ca|--max-snapshots|--snapshot-interval|--task-history-limit)
+ return
+ ;;
+ --listen-addr)
+ if [[ $cur == *: ]] ; then
+ COMPREPLY=( $( compgen -W "2377" -- "${cur##*:}" ) )
+ else
+ __docker_complete_local_interfaces --add 0.0.0.0
+ __docker_nospace
+ fi
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--advertise-addr --autolock --availability --cert-expiry --dispatcher-heartbeat --external-ca --force-new-cluster --help --listen-addr --max-snapshots --snapshot-interval --task-history-limit" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_swarm_join() {
+ case "$prev" in
+ --advertise-addr)
+ if [[ $cur == *: ]] ; then
+ COMPREPLY=( $( compgen -W "2377" -- "${cur##*:}" ) )
+ else
+ __docker_complete_local_interfaces
+ __docker_nospace
+ fi
+ return
+ ;;
+ --listen-addr)
+ if [[ $cur == *: ]] ; then
+ COMPREPLY=( $( compgen -W "2377" -- "${cur##*:}" ) )
+ else
+ __docker_complete_local_interfaces --add 0.0.0.0
+ __docker_nospace
+ fi
+ return
+ ;;
+ --availability)
+ COMPREPLY=( $( compgen -W "active drain pause" -- "$cur" ) )
+ return
+ ;;
+ --token)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--advertise-addr --availability --help --listen-addr --token" -- "$cur" ) )
+ ;;
+ *:)
+ COMPREPLY=( $( compgen -W "2377" -- "${cur##*:}" ) )
+ ;;
+ esac
+}
+
+_docker_swarm_join_token() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --quiet -q --rotate" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$( __docker_pos_first_nonflag )
+ if [ $cword -eq $counter ]; then
+ COMPREPLY=( $( compgen -W "manager worker" -- "$cur" ) )
+ fi
+ ;;
+ esac
+}
+
+_docker_swarm_leave() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_swarm_unlock() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_swarm_unlock_key() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --quiet -q --rotate" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_swarm_update() {
+ case "$prev" in
+ --cert-expiry|--dispatcher-heartbeat|--external-ca|--max-snapshots|--snapshot-interval|--task-history-limit)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--autolock --cert-expiry --dispatcher-heartbeat --external-ca --help --max-snapshots --snapshot-interval --task-history-limit" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_node() {
+ local subcommands="
+ demote
+ inspect
+ ls
+ promote
+ rm
+ ps
+ update
+ "
+ local aliases="
+ list
+ remove
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_node_demote() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_nodes --filter role=manager
+ esac
+}
+
+_docker_node_inspect() {
+ case "$prev" in
+ --format|-f)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--format -f --help --pretty" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_nodes --add self
+ esac
+}
+
+_docker_node_list() {
+ _docker_node_ls
+}
+
+_docker_node_ls() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ id)
+ __docker_complete_nodes --cur "${cur##*=}" --id
+ return
+ ;;
+ membership)
+ COMPREPLY=( $( compgen -W "accepted pending" -- "${cur##*=}" ) )
+ return
+ ;;
+ name)
+ __docker_complete_nodes --cur "${cur##*=}" --name
+ return
+ ;;
+ role)
+ COMPREPLY=( $( compgen -W "manager worker" -- "${cur##*=}" ) )
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -W "id label membership name role" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--filter -f --help --quiet -q" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_node_promote() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_nodes --filter role=worker
+ esac
+}
+
+_docker_node_remove() {
+ _docker_node_rm
+}
+
+_docker_node_rm() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_nodes
+ esac
+}
+
+_docker_node_ps() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ desired-state)
+ COMPREPLY=( $( compgen -W "accepted running shutdown" -- "${cur##*=}" ) )
+ return
+ ;;
+ name)
+ __docker_complete_services --cur "${cur##*=}" --name
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -W "desired-state id label name" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--filter -f --help --no-resolve --no-trunc" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_nodes --add self
+ ;;
+ esac
+}
+
+_docker_node_update() {
+ case "$prev" in
+ --availability)
+ COMPREPLY=( $( compgen -W "active drain pause" -- "$cur" ) )
+ return
+ ;;
+ --role)
+ COMPREPLY=( $( compgen -W "manager worker" -- "$cur" ) )
+ return
+ ;;
+ --label-add|--label-rm)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--availability --help --label-add --label-rm --role" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_nodes
+ esac
+}
+
+_docker_pause() {
+ _docker_container_pause
+}
+
+_docker_plugin() {
+ local subcommands="
+ create
+ disable
+ enable
+ inspect
+ install
+ ls
+ push
+ rm
+ set
+ upgrade
+ "
+ local aliases="
+ list
+ remove
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_plugin_create() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--compress --help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ # reponame
+ return
+ elif [ $cword -eq $((counter + 1)) ]; then
+ _filedir -d
+ fi
+ ;;
+ esac
+}
+
+_docker_plugin_disable() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_plugins_installed --filter enabled=true
+ fi
+ ;;
+ esac
+}
+
+_docker_plugin_enable() {
+ case "$prev" in
+ --timeout)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --timeout" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--timeout')
+ if [ $cword -eq $counter ]; then
+ __docker_complete_plugins_installed --filter enabled=false
+ fi
+ ;;
+ esac
+}
+
+_docker_plugin_inspect() {
+ case "$prev" in
+ --format|f)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_plugins_installed
+ ;;
+ esac
+}
+
+_docker_plugin_install() {
+ case "$prev" in
+ --alias)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--alias --disable --disable-content-trust=false --grant-all-permissions --help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_plugin_list() {
+ _docker_plugin_ls
+}
+
+_docker_plugin_ls() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ capability)
+ COMPREPLY=( $( compgen -W "authz ipamdriver networkdriver volumedriver" -- "${cur##*=}" ) )
+ return
+ ;;
+ enabled)
+ COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -S = -W "capability enabled" -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --format)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--filter -f --format --help --no-trunc --quiet -q" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_plugin_push() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_plugins_installed
+ fi
+ ;;
+ esac
+}
+
+_docker_plugin_remove() {
+ _docker_plugin_rm
+}
+
+_docker_plugin_rm() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_plugins_installed
+ ;;
+ esac
+}
+
+_docker_plugin_set() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_plugins_installed
+ fi
+ ;;
+ esac
+}
+
+_docker_plugin_upgrade() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--disable-content-trust --grant-all-permissions --help --skip-remote-check" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_plugins_installed
+ __ltrim_colon_completions "$cur"
+ elif [ $cword -eq $((counter + 1)) ]; then
+ local plugin_images="$(__docker_plugins_installed)"
+ COMPREPLY=( $(compgen -S : -W "${plugin_images%:*}" -- "$cur") )
+ __docker_nospace
+ fi
+ ;;
+ esac
+}
+
+
+_docker_port() {
+ _docker_container_port
+}
+
+_docker_ps() {
+ _docker_container_ls
+}
+
+_docker_pull() {
+ _docker_image_pull
+}
+
+_docker_push() {
+ _docker_image_push
+}
+
+_docker_rename() {
+ _docker_container_rename
+}
+
+_docker_restart() {
+ _docker_container_restart
+}
+
+_docker_rm() {
+ _docker_container_rm
+}
+
+_docker_rmi() {
+ _docker_image_rm
+}
+
+_docker_run() {
+ _docker_container_run
+}
+
+_docker_save() {
+ _docker_image_save
+}
+
+
+_docker_secret() {
+ local subcommands="
+ create
+ inspect
+ ls
+ rm
+ "
+ local aliases="
+ list
+ remove
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_secret_create() {
+ case "$prev" in
+ --label|-l)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --label -l" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_secret_inspect() {
+ case "$prev" in
+ --format|-f)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_secrets
+ ;;
+ esac
+}
+
+_docker_secret_list() {
+ _docker_secret_ls
+}
+
+_docker_secret_ls() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --quiet -q" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_secret_remove() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_secrets
+ ;;
+ esac
+}
+
+_docker_secret_rm() {
+ _docker_secret_remove
+}
+
+
+
+_docker_search() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ is-automated)
+ COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
+ return
+ ;;
+ is-official)
+ COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -S = -W "is-automated is-official stars" -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --limit)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--filter --help --limit --no-trunc" -- "$cur" ) )
+ ;;
+ esac
+}
+
+
+_docker_stack() {
+ local subcommands="
+ deploy
+ ls
+ ps
+ rm
+ services
+ "
+ local aliases="
+ down
+ list
+ remove
+ up
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_stack_deploy() {
+ case "$prev" in
+ --bundle-file)
+ if __docker_daemon_is_experimental ; then
+ _filedir dab
+ return
+ fi
+ ;;
+ --compose-file|-c)
+ _filedir yml
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ local options="--compose-file -c --help --with-registry-auth"
+ __docker_daemon_is_experimental && options+=" --bundle-file"
+ COMPREPLY=( $( compgen -W "$options" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_stack_down() {
+ _docker_stack_rm
+}
+
+_docker_stack_list() {
+ _docker_stack_ls
+}
+
+_docker_stack_ls() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_stack_ps() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ desired-state)
+ COMPREPLY=( $( compgen -W "accepted running shutdown" -- "${cur##*=}" ) )
+ return
+ ;;
+ id)
+ __docker_complete_stacks --cur "${cur##*=}" --id
+ return
+ ;;
+ name)
+ __docker_complete_stacks --cur "${cur##*=}" --name
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -S = -W "id name desired-state" -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--all -a --filter -f --help --no-resolve --no-trunc" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--filter|-f')
+ if [ $cword -eq $counter ]; then
+ __docker_complete_stacks
+ fi
+ ;;
+ esac
+}
+
+_docker_stack_remove() {
+ _docker_stack_rm
+}
+
+_docker_stack_rm() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag)
+ if [ $cword -eq $counter ]; then
+ __docker_complete_stacks
+ fi
+ ;;
+ esac
+}
+
+_docker_stack_services() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ id)
+ __docker_complete_services --cur "${cur##*=}" --id
+ return
+ ;;
+ label)
+ return
+ ;;
+ name)
+ __docker_complete_services --cur "${cur##*=}" --name
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -S = -W "id label name" -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --format)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--filter -f --format --help --quiet -q" -- "$cur" ) )
+ ;;
+ *)
+ local counter=$(__docker_pos_first_nonflag '--filter|-f|--format')
+ if [ $cword -eq $counter ]; then
+ __docker_complete_stacks
+ fi
+ ;;
+ esac
+}
+
+_docker_stack_up() {
+ _docker_stack_deploy
+}
+
+
+_docker_start() {
+ _docker_container_start
+}
+
+_docker_stats() {
+ _docker_container_stats
+}
+
+_docker_stop() {
+ _docker_container_stop
+}
+
+
+_docker_system() {
+ local subcommands="
+ df
+ events
+ info
+ prune
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_system_df() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help --verbose -v" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_system_events() {
+ local key=$(__docker_map_key_of_current_option '-f|--filter')
+ case "$key" in
+ container)
+ __docker_complete_containers_all --cur "${cur##*=}"
+ return
+ ;;
+ daemon)
+ local name=$(__docker_q info | sed -n 's/^\(ID\|Name\): //p')
+ COMPREPLY=( $( compgen -W "$name" -- "${cur##*=}" ) )
+ return
+ ;;
+ event)
+ COMPREPLY=( $( compgen -W "
+ attach
+ commit
+ connect
+ copy
+ create
+ delete
+ destroy
+ detach
+ die
+ disconnect
+ exec_create
+ exec_detach
+ exec_start
+ export
+ health_status
+ import
+ kill
+ load
+ mount
+ oom
+ pause
+ pull
+ push
+ reload
+ rename
+ resize
+ restart
+ save
+ start
+ stop
+ tag
+ top
+ unmount
+ unpause
+ untag
+ update
+ " -- "${cur##*=}" ) )
+ return
+ ;;
+ image)
+ cur="${cur##*=}"
+ __docker_complete_images
+ return
+ ;;
+ network)
+ __docker_complete_networks --cur "${cur##*=}"
+ return
+ ;;
+ type)
+ COMPREPLY=( $( compgen -W "container daemon image network volume" -- "${cur##*=}" ) )
+ return
+ ;;
+ volume)
+ __docker_complete_volumes --cur "${cur##*=}"
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -S = -W "container daemon event image label network type volume" -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --since|--until)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--filter -f --help --since --until --format" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_system_info() {
+ case "$prev" in
+ --format|-f)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_system_prune() {
+ case "$prev" in
+ --filter)
+ COMPREPLY=( $( compgen -W "until" -S = -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--all -a --force -f --filter --help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+
+_docker_tag() {
+ _docker_image_tag
+}
+
+_docker_unpause() {
+ _docker_container_unpause
+}
+
+_docker_update() {
+ _docker_container_update
+}
+
+_docker_top() {
+ _docker_container_top
+}
+
+_docker_version() {
+ case "$prev" in
+ --format|-f)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_volume_create() {
+ case "$prev" in
+ --driver|-d)
+ __docker_complete_plugins_bundled --type Volume
+ return
+ ;;
+ --label|--opt|-o)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--driver -d --help --label --opt -o" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_volume_inspect() {
+ case "$prev" in
+ --format|-f)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_volumes
+ ;;
+ esac
+}
+
+_docker_volume_list() {
+ _docker_volume_ls
+}
+
+_docker_volume_ls() {
+ local key=$(__docker_map_key_of_current_option '--filter|-f')
+ case "$key" in
+ dangling)
+ COMPREPLY=( $( compgen -W "true false" -- "${cur##*=}" ) )
+ return
+ ;;
+ driver)
+ __docker_complete_plugins_bundled --cur "${cur##*=}" --type Volume
+ return
+ ;;
+ name)
+ __docker_complete_volumes --cur "${cur##*=}"
+ return
+ ;;
+ esac
+
+ case "$prev" in
+ --filter|-f)
+ COMPREPLY=( $( compgen -S = -W "dangling driver label name" -- "$cur" ) )
+ __docker_nospace
+ return
+ ;;
+ --format)
+ return
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--filter -f --format --help --quiet -q" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_volume_prune() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_volume_remove() {
+ _docker_volume_rm
+}
+
+_docker_volume_rm() {
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) )
+ ;;
+ *)
+ __docker_complete_volumes
+ ;;
+ esac
+}
+
+_docker_volume() {
+ local subcommands="
+ create
+ inspect
+ ls
+ prune
+ rm
+ "
+ local aliases="
+ list
+ remove
+ "
+ __docker_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
+
+_docker_wait() {
+ _docker_container_wait
+}
+
+_docker() {
+ local previous_extglob_setting=$(shopt -p extglob)
+ shopt -s extglob
+
+ local management_commands=(
+ container
+ image
+ network
+ node
+ plugin
+ secret
+ service
+ stack
+ system
+ volume
+ )
+
+ local top_level_commands=(
+ build
+ login
+ logout
+ run
+ search
+ version
+ )
+
+ local legacy_commands=(
+ commit
+ cp
+ create
+ diff
+ events
+ exec
+ export
+ history
+ images
+ import
+ info
+ inspect
+ kill
+ load
+ logs
+ pause
+ port
+ ps
+ pull
+ push
+ rename
+ restart
+ rm
+ rmi
+ save
+ start
+ stats
+ stop
+ swarm
+ tag
+ top
+ unpause
+ update
+ wait
+ )
+
+ local experimental_commands=(
+ checkpoint
+ deploy
+ )
+
+ local commands=(${management_commands[*]} ${top_level_commands[*]})
+ [ -z "$DOCKER_HIDE_LEGACY_COMMANDS" ] && commands+=(${legacy_commands[*]})
+
+ # These options are valid as global options for all client commands
+ # and valid as command options for `docker daemon`
+ local global_boolean_options="
+ --debug -D
+ --tls
+ --tlsverify
+ "
+ local global_options_with_args="
+ --config
+ --host -H
+ --log-level -l
+ --tlscacert
+ --tlscert
+ --tlskey
+ "
+
+ local host config daemon_os
+
+ COMPREPLY=()
+ local cur prev words cword
+ _get_comp_words_by_ref -n : cur prev words cword
+
+ local command='docker' command_pos=0 subcommand_pos
+ local counter=1
+ while [ $counter -lt $cword ]; do
+ case "${words[$counter]}" in
+ # save host so that completion can use custom daemon
+ --host|-H)
+ (( counter++ ))
+ host="${words[$counter]}"
+ ;;
+ # save config so that completion can use custom configuration directories
+ --config)
+ (( counter++ ))
+ config="${words[$counter]}"
+ ;;
+ $(__docker_to_extglob "$global_options_with_args") )
+ (( counter++ ))
+ ;;
+ -*)
+ ;;
+ =)
+ (( counter++ ))
+ ;;
+ *)
+ command="${words[$counter]}"
+ command_pos=$counter
+ break
+ ;;
+ esac
+ (( counter++ ))
+ done
+
+ local binary="${words[0]}"
+ if [[ $binary == ?(*/)dockerd ]] ; then
+ # for the dockerd binary, we reuse completion of `docker daemon`.
+ # dockerd does not have subcommands and global options.
+ command=daemon
+ command_pos=0
+ fi
+
+ local completions_func=_docker_${command//-/_}
+ declare -F $completions_func >/dev/null && $completions_func
+
+ eval "$previous_extglob_setting"
+ return 0
+}
+
+eval "$__docker_previous_extglob_setting"
+unset __docker_previous_extglob_setting
+
+complete -F _docker docker docker.exe dockerd dockerd.exe
diff --git a/roles/docker/files/docker-tag b/roles/docker/files/docker-tag
new file mode 100644
index 0000000..cce6fff
--- /dev/null
+++ b/roles/docker/files/docker-tag
@@ -0,0 +1,2 @@
+#!/bin/bash
+curl -s -S "https://registry.hub.docker.com/v2/repositories/$@/tags/" | jq '."results"[]["name"]' |sort
diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml
new file mode 100644
index 0000000..19d8033
--- /dev/null
+++ b/roles/docker/tasks/main.yml
@@ -0,0 +1,99 @@
+- name: 获取是否运行名为'kubeasz'的容器
+ shell: 'systemctl status docker|grep Active && docker ps|grep kubeasz || echo "NOT FOUND"'
+ register: install_info
+ tags: upgrade_docker, download_docker
+
+# 18.09.x 版本二进制名字有变化,需要做判断
+- name: 获取docker版本信息
+ shell: "{{ base_dir }}/bin/dockerd --version|cut -d' ' -f3"
+ register: docker_ver
+ connection: local
+ run_once: true
+ tags: upgrade_docker, download_docker
+
+- name: 转换docker版本信息为浮点数
+ set_fact:
+ DOCKER_VER: "{{ docker_ver.stdout.split('.')[0]|int + docker_ver.stdout.split('.')[1]|int/100 }}"
+ tags: upgrade_docker, download_docker
+
+#- name: debug info
+# debug: var="DOCKER_VER"
+# tags: upgrade_docker, download_docker
+
+- block:
+ - name: 准备docker相关目录
+ file: name={{ item }} state=directory
+ with_items:
+ - "{{ bin_dir }}"
+ - /etc/docker
+
+ - name: 下载 docker 二进制文件
+ copy: src={{ base_dir }}/bin/{{ item }} dest={{ bin_dir }}/{{ item }} mode=0755
+ with_items:
+ - docker-containerd
+ - docker-containerd-shim
+ - docker-init
+ - docker-runc
+ - docker
+ - docker-containerd-ctr
+ - dockerd
+ - docker-proxy
+ tags: upgrade_docker, download_docker
+ when: "DOCKER_VER < 18.09"
+
+ - name: 下载 docker 二进制文件(>= 18.09.x)
+ copy: src={{ base_dir }}/bin/{{ item }} dest={{ bin_dir }}/{{ item }} mode=0755
+ with_items:
+ - containerd
+ - containerd-shim
+ - docker-init
+ - runc
+ - docker
+ - ctr
+ - dockerd
+ - docker-proxy
+ tags: upgrade_docker, download_docker
+ when: "DOCKER_VER >= 18.09"
+
+ - name: docker命令自动补全
+ copy: src=docker dest=/etc/bash_completion.d/docker mode=0644
+
+ - name: docker国内镜像加速
+ template: src=daemon.json.j2 dest=/etc/docker/daemon.json
+
+ - name: flush-iptables
+ shell: "iptables -P INPUT ACCEPT \
+ && iptables -F && iptables -X \
+ && iptables -F -t nat && iptables -X -t nat \
+ && iptables -F -t raw && iptables -X -t raw \
+ && iptables -F -t mangle && iptables -X -t mangle"
+
+ - name: 创建docker的systemd unit文件
+ template: src=docker.service.j2 dest=/etc/systemd/system/docker.service
+ tags: upgrade_docker, download_docker
+
+ - name: 开机启用docker 服务
+ shell: systemctl enable docker
+ ignore_errors: true
+
+ - name: 开启docker 服务
+ shell: systemctl daemon-reload && systemctl restart docker
+ tags: upgrade_docker
+
+ ## 可选 ------安装docker查询镜像 tag的小工具----
+ # 先要安装轻量JSON处理程序‘jq’,已在 prepare 节点安装
+ - name: 下载 docker-tag
+ copy: src=docker-tag dest={{ bin_dir }}/docker-tag mode=0755
+
+ - name: 轮询等待docker服务运行
+ shell: "systemctl status docker.service|grep Active"
+ register: docker_status
+ until: '"running" in docker_status.stdout'
+ retries: 8
+ delay: 2
+ tags: upgrade_docker
+
+ # 配置 docker 命令软链接,方便单独安装 docker
+ - name: 配置 docker 命令软链接
+ file: src={{ bin_dir }}/docker dest=/usr/bin/docker state=link
+ when: "'kubeasz' not in install_info.stdout"
diff --git a/roles/docker/templates/daemon.json.j2 b/roles/docker/templates/daemon.json.j2
new file mode 100644
index 0000000..ff627fb
--- /dev/null
+++ b/roles/docker/templates/daemon.json.j2
@@ -0,0 +1,16 @@
+{
+ "registry-mirrors": ["{{ REG_MIRROR_1 }}", "{{ REG_MIRROR_2 }}"],
+ "max-concurrent-downloads": 10,
+ "log-driver": "{{ LOG_DRIVER }}",
+ "log-level": "{{ LOG_LEVEL }}",
+ "log-opts": {
+ "max-size": "{{ LOG_MAX_SIZE }}",
+ "max-file": "{{ LOG_MAX_FILE }}"
+ },
+ "data-root": "{{ STORAGE_DIR }}"
+
+ {% if ENABLE_REMOTE_API %}
+ , "hosts": ["tcp://0.0.0.0:2376", "unix:///var/run/docker.sock"]
+ {% endif %}
+
+}
diff --git a/roles/docker/templates/docker.service.j2 b/roles/docker/templates/docker.service.j2
new file mode 100644
index 0000000..c2cd4ce
--- /dev/null
+++ b/roles/docker/templates/docker.service.j2
@@ -0,0 +1,19 @@
+[Unit]
+Description=Docker Application Container Engine
+Documentation=http://docs.docker.io
+
+[Service]
+Environment="PATH={{ bin_dir }}:/bin:/sbin:/usr/bin:/usr/sbin"
+ExecStart={{ bin_dir }}/dockerd
+ExecStartPost=/sbin/iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT
+ExecReload=/bin/kill -s HUP $MAINPID
+Restart=on-failure
+RestartSec=5
+LimitNOFILE=infinity
+LimitNPROC=infinity
+LimitCORE=infinity
+Delegate=yes
+KillMode=process
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/etcd/clean-etcd.yml b/roles/etcd/clean-etcd.yml
new file mode 100644
index 0000000..f0b79f9
--- /dev/null
+++ b/roles/etcd/clean-etcd.yml
@@ -0,0 +1,18 @@
+# WARNNING: clean 'etcd' nodes service & data
+- hosts:
+ - etcd
+ tasks:
+ - name: stop and disable etcd service
+ service:
+ name: etcd
+ state: stopped
+ enabled: no
+ ignore_errors: true
+
+ - name: remove files and dirs
+ file: name={{ item }} state=absent
+ with_items:
+ - "/var/lib/etcd"
+ - "/etc/etcd/"
+ - "/backup/k8s"
+ - "/etc/systemd/system/etcd.service"
diff --git a/roles/etcd/defaults/main.yml b/roles/etcd/defaults/main.yml
new file mode 100644
index 0000000..d6a8d3c
--- /dev/null
+++ b/roles/etcd/defaults/main.yml
@@ -0,0 +1,6 @@
+# etcd 集群间通信的IP和端口, 根据etcd组成员自动生成
+TMP_NODES: "{% for h in groups['etcd'] %}{{ hostvars[h]['NODE_NAME'] }}=https://{{ h }}:2380,{% endfor %}"
+ETCD_NODES: "{{ TMP_NODES.rstrip(',') }}"
+
+# etcd 集群初始状态 new/existing
+CLUSTER_STATE: "new"
diff --git a/roles/etcd/tasks/main.yml b/roles/etcd/tasks/main.yml
new file mode 100644
index 0000000..7cafffe
--- /dev/null
+++ b/roles/etcd/tasks/main.yml
@@ -0,0 +1,62 @@
+- name: prepare some dirs
+ file: name={{ item }} state=directory
+ with_items:
+ - "{{ bin_dir }}"
+ - "{{ ca_dir }}"
+ - "/etc/etcd/ssl" # etcd 证书目录
+ - "/var/lib/etcd" # etcd 工作目录
+
+- name: 下载etcd二进制文件
+ copy: src={{ base_dir }}/bin/{{ item }} dest={{ bin_dir }}/{{ item }} mode=0755
+ with_items:
+ - etcd
+ - etcdctl
+ tags: upgrade_etcd
+
+- name: 分发证书相关
+ synchronize: src={{ ca_dir }}/{{ item }} dest={{ ca_dir }}/{{ item }}
+ with_items:
+ - ca.pem
+ - ca-key.pem
+ - ca.csr
+ - ca-config.json
+ delegate_to: "{{ groups.deploy[0] }}"
+
+# 注册变量p,根据p的stat信息判断是否已经生成过etcd证书,如果没有,下一步生成证书
+# 如果已经有etcd证书,为了保证整个安装的幂等性,跳过证书生成的步骤
+- name: 读取etcd证书stat信息
+ stat: path="/etc/etcd/ssl/etcd.pem"
+ register: p
+
+- name: 创建etcd证书请求
+ template: src=etcd-csr.json.j2 dest=/etc/etcd/ssl/etcd-csr.json
+ when: p.stat.isreg is not defined
+
+- name: 创建 etcd证书和私钥
+ when: p.stat.isreg is not defined
+ shell: "cd /etc/etcd/ssl && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes etcd-csr.json | {{ bin_dir }}/cfssljson -bare etcd"
+
+- name: 创建etcd的systemd unit文件
+ template: src=etcd.service.j2 dest=/etc/systemd/system/etcd.service
+ tags: upgrade_etcd
+
+- name: 开机启用etcd服务
+ shell: systemctl enable etcd
+ ignore_errors: true
+
+- name: 开启etcd服务
+ shell: systemctl daemon-reload && systemctl restart etcd
+ ignore_errors: true
+ tags: upgrade_etcd
+
+- name: 以轮询的方式等待服务同步完成
+ shell: "systemctl status etcd.service|grep Active"
+ register: etcd_status
+ until: '"running" in etcd_status.stdout'
+ retries: 8
+ delay: 8
+ tags: upgrade_etcd
diff --git a/roles/etcd/templates/etcd-csr.json.j2 b/roles/etcd/templates/etcd-csr.json.j2
new file mode 100644
index 0000000..674a334
--- /dev/null
+++ b/roles/etcd/templates/etcd-csr.json.j2
@@ -0,0 +1,20 @@
+{
+ "CN": "etcd",
+ "hosts": [
+ "127.0.0.1",
+ "{{ inventory_hostname }}"
+ ],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/etcd/templates/etcd.service.j2 b/roles/etcd/templates/etcd.service.j2
new file mode 100644
index 0000000..46d3bcc
--- /dev/null
+++ b/roles/etcd/templates/etcd.service.j2
@@ -0,0 +1,32 @@
+[Unit]
+Description=Etcd Server
+After=network.target
+After=network-online.target
+Wants=network-online.target
+Documentation=https://github.com/coreos
+
+[Service]
+Type=notify
+WorkingDirectory=/var/lib/etcd/
+ExecStart={{ bin_dir }}/etcd \
+ --name={{ NODE_NAME }} \
+ --cert-file=/etc/etcd/ssl/etcd.pem \
+ --key-file=/etc/etcd/ssl/etcd-key.pem \
+ --peer-cert-file=/etc/etcd/ssl/etcd.pem \
+ --peer-key-file=/etc/etcd/ssl/etcd-key.pem \
+ --trusted-ca-file={{ ca_dir }}/ca.pem \
+ --peer-trusted-ca-file={{ ca_dir }}/ca.pem \
+ --initial-advertise-peer-urls=https://{{ inventory_hostname }}:2380 \
+ --listen-peer-urls=https://{{ inventory_hostname }}:2380 \
+ --listen-client-urls=https://{{ inventory_hostname }}:2379,http://127.0.0.1:2379 \
+ --advertise-client-urls=https://{{ inventory_hostname }}:2379 \
+ --initial-cluster-token=etcd-cluster-0 \
+ --initial-cluster={{ ETCD_NODES }} \
+ --initial-cluster-state={{ CLUSTER_STATE }} \
+ --data-dir=/var/lib/etcd
+Restart=on-failure
+RestartSec=5
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/ex-lb/clean-ex-lb.yml b/roles/ex-lb/clean-ex-lb.yml
new file mode 100644
index 0000000..cdff59c
--- /dev/null
+++ b/roles/ex-lb/clean-ex-lb.yml
@@ -0,0 +1,36 @@
+- hosts:
+ - ex-lb
+ tasks:
+ - name: stop and disable chrony in Ubuntu
+ service: name=chrony state=stopped enabled=no
+ ignore_errors: true
+ tags: rm_ntp
+ when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"
+
+ - name: stop and disable chronyd in CentOS/RedHat
+ service: name=chronyd state=stopped enabled=no
+ ignore_errors: true
+ tags: rm_ntp
+ when: ansible_distribution == "CentOS" or ansible_distribution == "RedHat"
+
+ - name: stop keepalived service
+ service: name=keepalived state=stopped enabled=no
+ ignore_errors: true
+
+ - name: stop haproxy service
+ service: name=haproxy state=stopped enabled=no
+ ignore_errors: true
+
+ - name: remove files and dirs
+ file: name={{ item }} state=absent
+ with_items:
+ - "/etc/haproxy"
+ - "/etc/keepalived"
+
+ - name: clean 'ENV PATH'
+ lineinfile:
+ dest: ~/.bashrc
+ state: absent
+ regexp: '{{ item }}'
+ with_items:
+ - 'kubeasz'
diff --git a/roles/ex-lb/defaults/main.yml b/roles/ex-lb/defaults/main.yml
new file mode 100644
index 0000000..bf1e6b0
--- /dev/null
+++ b/roles/ex-lb/defaults/main.yml
@@ -0,0 +1,15 @@
+# 区分多个instance的VRRP组播,同网段不能重复,取值在0-255之间
+# 因项目已设置vrrp报文单播模式,所以这个ROUTER_ID 即便同网段里面有重复也没关系
+ROUTER_ID: 222
+
+# haproxy负载均衡算法,常见如下:
+# "roundrobin": 基于服务器权重的轮询
+# "leastconn": 基于服务器最小连接数
+# "source": 基于请求源IP地址
+# "uri": 基于请求的URI
+BALANCE_ALG: "roundrobin"
+
+# 启用 ingress NodePort服务的负载均衡 (yes/no)
+INGRESS_NODEPORT_LB: "yes"
+# 启用 ingress tls NodePort服务的负载均衡 (yes/no)
+INGRESS_TLS_NODEPORT_LB: "yes"
diff --git a/roles/ex-lb/ex-lb.yml b/roles/ex-lb/ex-lb.yml
new file mode 100644
index 0000000..20103c9
--- /dev/null
+++ b/roles/ex-lb/ex-lb.yml
@@ -0,0 +1,5 @@
+- hosts: ex-lb
+ roles:
+ - { role: chrony, when: "hostvars[groups.deploy[0]]['NTP_ENABLED'] == 'yes'" }
+ - prepare
+ - ex-lb
diff --git a/roles/ex-lb/tasks/main.yml b/roles/ex-lb/tasks/main.yml
new file mode 100644
index 0000000..b585ff7
--- /dev/null
+++ b/roles/ex-lb/tasks/main.yml
@@ -0,0 +1,62 @@
+# 自动设置LB节点变量'LB_IF'
+- name: 注册变量 LB_IF_TMP
+ shell: "ip a|grep '{{ inventory_hostname }}/'|awk '{print $NF}'"
+ register: LB_IF_TMP
+
+- name: 设置变量 LB_IF
+ set_fact: LB_IF="{{ LB_IF_TMP.stdout }}"
+
+- name: apt更新缓存刷新
+ apt: update_cache=yes cache_valid_time=72000
+ when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version|int >= 16
+
+- name: 安装 haproxy
+ package: name=haproxy state=latest
+
+- name: 创建haproxy配置目录
+ file: name=/etc/haproxy state=directory
+
+- name: 修改centos的haproxy.service
+ template: src=haproxy.service.j2 dest=/usr/lib/systemd/system/haproxy.service
+ when: (ansible_distribution == "CentOS" or ansible_distribution == "RedHat") and ansible_distribution_major_version == "7"
+ tags: restart_lb
+
+- name: 配置 haproxy
+ template: src=haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg
+ tags: restart_lb
+
+- name: 安装 keepalived
+ package: name=keepalived state=latest
+
+- name: 创建keepalived配置目录
+ file: name=/etc/keepalived state=directory
+
+- name: 配置 keepalived 主节点
+ template: src=keepalived-master.conf.j2 dest=/etc/keepalived/keepalived.conf
+ when: LB_ROLE == "master"
+ tags: restart_lb
+
+- name: 配置 keepalived 备节点
+ template: src=keepalived-backup.conf.j2 dest=/etc/keepalived/keepalived.conf
+ when: LB_ROLE == "backup"
+ tags: restart_lb
+
+- name: daemon-reload for haproxy.service
+ shell: systemctl daemon-reload
+ tags: restart_lb
+
+- name: 开机启用haproxy服务
+ shell: systemctl enable haproxy
+ ignore_errors: true
+
+- name: 重启haproxy服务
+ shell: systemctl restart haproxy
+ tags: restart_lb
+
+- name: 开机启用keepalived服务
+ shell: systemctl enable keepalived
+ ignore_errors: true
+
+- name: 重启keepalived服务
+ shell: systemctl restart keepalived
+ tags: restart_lb
diff --git a/roles/ex-lb/templates/haproxy.cfg.j2 b/roles/ex-lb/templates/haproxy.cfg.j2
new file mode 100644
index 0000000..f4cbe97
--- /dev/null
+++ b/roles/ex-lb/templates/haproxy.cfg.j2
@@ -0,0 +1,39 @@
+global
+ log /dev/log local0
+ log /dev/log local1 notice
+ chroot /var/lib/haproxy
+ stats socket /run/haproxy/admin.sock mode 660 level admin
+ stats timeout 30s
+ user haproxy
+ group haproxy
+ daemon
+ nbproc 1
+
+defaults
+ log global
+ timeout connect 5000
+ timeout client 10m
+ timeout server 10m
+
+{% if INGRESS_NODEPORT_LB == "yes" %}
+
+listen ingress-node
+ bind 0.0.0.0:80
+ mode tcp
+ option tcplog
+ balance {{ BALANCE_ALG }}
+{% for host in groups['kube-node'] %}
+ server {{ host }} {{ host }}:23456 check inter 2000 fall 2 rise 2 weight 1
+{% endfor %}
+{% endif %}
+{% if INGRESS_TLS_NODEPORT_LB == "yes" %}
+
+listen ingress-node-tls
+ bind 0.0.0.0:443
+ mode tcp
+ option tcplog
+ balance {{ BALANCE_ALG }}
+{% for host in groups['kube-node'] %}
+ server {{ host }} {{ host }}:23457 check inter 2000 fall 2 rise 2 weight 1
+{% endfor %}
+{% endif %}
diff --git a/roles/ex-lb/templates/haproxy.service.j2 b/roles/ex-lb/templates/haproxy.service.j2
new file mode 100644
index 0000000..938e1bd
--- /dev/null
+++ b/roles/ex-lb/templates/haproxy.service.j2
@@ -0,0 +1,13 @@
+[Unit]
+Description=HAProxy Load Balancer
+After=syslog.target network.target
+
+[Service]
+EnvironmentFile=/etc/sysconfig/haproxy
+ExecStartPre=/usr/bin/mkdir -p /run/haproxy
+ExecStart=/usr/sbin/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid $OPTIONS
+ExecReload=/bin/kill -USR2 $MAINPID
+KillMode=mixed
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/ex-lb/templates/keepalived-backup.conf.j2 b/roles/ex-lb/templates/keepalived-backup.conf.j2
new file mode 100644
index 0000000..ce315cd
--- /dev/null
+++ b/roles/ex-lb/templates/keepalived-backup.conf.j2
@@ -0,0 +1,30 @@
+global_defs {
+ router_id lb-backup-{{ inventory_hostname }}
+}
+
+vrrp_script check-haproxy {
+ script "killall -0 haproxy"
+ interval 5
+ weight -60
+}
+
+vrrp_instance VI-kube-master {
+ state BACKUP
+ priority {{ 119 | random(61, 1) }}
+ unicast_src_ip {{ inventory_hostname }}
+ unicast_peer {
+{% for h in groups['ex-lb'] %}{% if h != inventory_hostname %}
+ {{ h }}
+{% endif %}{% endfor %}
+ }
+ dont_track_primary
+ interface {{ LB_IF }}
+ virtual_router_id {{ ROUTER_ID }}
+ advert_int 3
+ track_script {
+ check-haproxy
+ }
+ virtual_ipaddress {
+ {{ EX_VIP }}
+ }
+}
diff --git a/roles/ex-lb/templates/keepalived-master.conf.j2 b/roles/ex-lb/templates/keepalived-master.conf.j2
new file mode 100644
index 0000000..e16e862
--- /dev/null
+++ b/roles/ex-lb/templates/keepalived-master.conf.j2
@@ -0,0 +1,30 @@
+global_defs {
+ router_id lb-master-{{ inventory_hostname }}
+}
+
+vrrp_script check-haproxy {
+ script "killall -0 haproxy"
+ interval 5
+ weight -60
+}
+
+vrrp_instance VI-kube-master {
+ state MASTER
+ priority 120
+ unicast_src_ip {{ inventory_hostname }}
+ unicast_peer {
+{% for h in groups['ex-lb'] %}{% if h != inventory_hostname %}
+ {{ h }}
+{% endif %}{% endfor %}
+ }
+ dont_track_primary
+ interface {{ LB_IF }}
+ virtual_router_id {{ ROUTER_ID }}
+ advert_int 3
+ track_script {
+ check-haproxy
+ }
+ virtual_ipaddress {
+ {{ EX_VIP }}
+ }
+}
diff --git a/roles/flannel/defaults/main.yml b/roles/flannel/defaults/main.yml
new file mode 100644
index 0000000..258143b
--- /dev/null
+++ b/roles/flannel/defaults/main.yml
@@ -0,0 +1,15 @@
+# 部分flannel配置,详见roles/flannel/templates/kube-flannel.yaml.j2
+
+# 如果 node 节点有多块网卡,请设置 true
+# 另外发现设置为 true 时能够解决v1.10使用ipvs偶尔出现pod内‘dial tcp 10.68.0.1:443: i/o timeout’的 bug
+NODE_WITH_MULTIPLE_NETWORKS: "true"
+
+# 设置flannel 后端
+#FLANNEL_BACKEND: "host-gw"
+FLANNEL_BACKEND: "vxlan"
+
+#flanneld_image: "quay.io/coreos/flannel:v0.10.0-amd64"
+flanneld_image: "jmgao1983/flannel:v0.11.0-amd64"
+
+# 离线镜像tar包
+flannel_offline: "flannel_v0.11.0-amd64.tar"
diff --git a/roles/flannel/tasks/main.yml b/roles/flannel/tasks/main.yml
new file mode 100644
index 0000000..b22e6e1
--- /dev/null
+++ b/roles/flannel/tasks/main.yml
@@ -0,0 +1,72 @@
+- block:
+ - name: 在deploy 节点创建相关目录
+ file: name=/opt/kube/kube-system/flannel state=directory
+
+ - name: 配置 flannel DaemonSet yaml文件
+ template: src=kube-flannel.yaml.j2 dest=/opt/kube/kube-system/flannel/kube-flannel.yaml
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+- name: 检查是否已下载离线flannel镜像
+ command: "ls {{ base_dir }}/down"
+ register: download_info
+ connection: local
+ run_once: true
+
+- name: 创建flannel cni 相关目录
+ file: name={{ item }} state=directory
+ with_items:
+ - /etc/cni/net.d
+ - /opt/kube/images
+
+- name: 下载flannel cni plugins
+ copy: src={{ base_dir }}/bin/{{ item }} dest={{ bin_dir }}/{{ item }} mode=0755
+ with_items:
+ - bridge
+ - flannel
+ - host-local
+ - loopback
+ - portmap
+
+# 【可选】推送离线docker 镜像,可以忽略执行错误
+- name: 尝试推送离线docker 镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ item }} dest=/opt/kube/images/{{ item }}
+ when: 'item in download_info.stdout'
+ with_items:
+ - "pause_3.1.tar"
+ - "{{ flannel_offline }}"
+ ignore_errors: true
+
+- name: 获取flannel离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+# 如果目录下有离线镜像,就把它导入到node节点上
+- name: 导入 flannel的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ item }}"
+ when: 'item in image_info.stdout'
+ with_items:
+ - "pause_3.1.tar"
+ - "{{ flannel_offline }}"
+ ignore_errors: true
+
+# 只需单节点执行一次
+- name: 运行 flannel网络
+ shell: "{{ bin_dir }}/kubectl apply -f /opt/kube/kube-system/flannel/ && sleep 5"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+# 删除原有cni配置
+- name: 删除默认cni配置
+ file: path=/etc/cni/net.d/10-default.conf state=absent
+
+# 等待网络插件部署成功,视下载镜像速度而定
+- name: 轮询等待flannel 运行,视下载镜像速度而定
+ shell: "{{ bin_dir }}/kubectl get pod -n kube-system -o wide|grep 'flannel'|grep ' {{ inventory_hostname }} '|awk '{print $3}'"
+ register: pod_status
+ until: pod_status.stdout == "Running"
+ delegate_to: "{{ groups.deploy[0] }}"
+ retries: 15
+ delay: 8
+ ignore_errors: true
+
diff --git a/roles/flannel/templates/kube-flannel.yaml.j2 b/roles/flannel/templates/kube-flannel.yaml.j2
new file mode 100644
index 0000000..21d701e
--- /dev/null
+++ b/roles/flannel/templates/kube-flannel.yaml.j2
@@ -0,0 +1,166 @@
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: flannel
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - pods
+ verbs:
+ - get
+ - apiGroups:
+ - ""
+ resources:
+ - nodes
+ verbs:
+ - list
+ - watch
+ - apiGroups:
+ - ""
+ resources:
+ - nodes/status
+ verbs:
+ - patch
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: flannel
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: flannel
+subjects:
+- kind: ServiceAccount
+ name: flannel
+ namespace: kube-system
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: flannel
+ namespace: kube-system
+---
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: kube-flannel-cfg
+ namespace: kube-system
+ labels:
+ tier: node
+ app: flannel
+data:
+ cni-conf.json: |
+ {
+ "name": "cbr0",
+ "plugins": [
+ {
+ "type": "flannel",
+ "delegate": {
+ "hairpinMode": true,
+ "isDefaultGateway": true
+ }
+ },
+ {
+ "type": "portmap",
+ "capabilities": {
+ "portMappings": true
+ }
+ }
+ ]
+ }
+ net-conf.json: |
+ {
+ "Network": "{{ CLUSTER_CIDR }}",
+ "Backend": {
+ "Type": "{{ FLANNEL_BACKEND }}"
+ }
+ }
+---
+apiVersion: extensions/v1beta1
+kind: DaemonSet
+metadata:
+ name: kube-flannel-ds-amd64
+ namespace: kube-system
+ labels:
+ tier: node
+ app: flannel
+spec:
+ template:
+ metadata:
+ labels:
+ tier: node
+ app: flannel
+ spec:
+ hostNetwork: true
+ nodeSelector:
+ beta.kubernetes.io/arch: amd64
+ tolerations:
+ - operator: Exists
+ effect: NoSchedule
+ serviceAccountName: flannel
+ initContainers:
+ - name: install-cni
+ image: {{ flanneld_image }}
+ command:
+ - cp
+ args:
+ - -f
+ - /etc/kube-flannel/cni-conf.json
+ - /etc/cni/net.d/10-flannel.conflist
+ volumeMounts:
+ - name: cni
+ mountPath: /etc/cni/net.d
+ - name: flannel-cfg
+ mountPath: /etc/kube-flannel/
+ containers:
+ - name: kube-flannel
+ image: {{ flanneld_image }}
+ command:
+ - /opt/bin/flanneld
+ args:
+ - --ip-masq
+ - --kube-subnet-mgr
+ resources:
+ requests:
+ cpu: "100m"
+ memory: "50Mi"
+ limits:
+ cpu: "100m"
+ memory: "50Mi"
+ securityContext:
+ privileged: true
+ env:
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: POD_NAMESPACE
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+{% if NODE_WITH_MULTIPLE_NETWORKS == 'true' %}
+ # if hosts have multiple net interfaces, set following two ENVs
+ - name: KUBERNETES_SERVICE_HOST
+ value: "{{ MASTER_IP }}"
+ #value: "{{ KUBE_APISERVER.split(':')[1].lstrip('/') }}"
+ - name: KUBERNETES_SERVICE_PORT
+ value: "{{ KUBE_APISERVER.split(':')[2] }}"
+{% endif %}
+ volumeMounts:
+ - name: run
+ mountPath: /run
+ - name: flannel-cfg
+ mountPath: /etc/kube-flannel/
+ volumes:
+ - name: run
+ hostPath:
+ path: /run
+ - name: cni
+ hostPath:
+ path: /etc/cni/net.d
+ - name: flannel-cfg
+ configMap:
+ name: kube-flannel-cfg
diff --git a/roles/harbor/defaults/main.yml b/roles/harbor/defaults/main.yml
new file mode 100644
index 0000000..4f54702
--- /dev/null
+++ b/roles/harbor/defaults/main.yml
@@ -0,0 +1,6 @@
+# harbor version,完整版本号,目前支持 v1.5.x 和 v1.6.x
+HARBOR_VER: "v1.6.3"
+
+# harbor 主版本号,目前支持主版本号 v1.5/v1.6
+# 从完整版本号提取出主版本号 v1.5/v1.6
+HARBOR_VER_MAIN: "{{ HARBOR_VER.split('.')[0] }}.{{ HARBOR_VER.split('.')[1] }}"
diff --git a/roles/harbor/tasks/main.yml b/roles/harbor/tasks/main.yml
new file mode 100644
index 0000000..4edb900
--- /dev/null
+++ b/roles/harbor/tasks/main.yml
@@ -0,0 +1,53 @@
+- name: 创建data目录
+ file:
+ path: /data
+ state: directory
+ mode: 0755
+
+# 注册变量result,如果/data目录下存在registry目录说明已经安装过harbor,则不进行安装
+- name: 注册变量result
+ command: ls /data
+ register: result
+
+- block:
+ - name: 下发docker compose二进制文件
+ copy: src={{ base_dir }}/bin/docker-compose dest={{ bin_dir }}/docker-compose mode=0755
+
+ - name: 下发harbor离线安装包
+ copy:
+ src: "{{ base_dir }}/down/harbor-offline-installer-{{ HARBOR_VER }}.tgz"
+ dest: "/data/harbor-offline-installer-{{ HARBOR_VER }}.tgz"
+
+ - name: 解压harbor离线安装包
+ shell: "cd /data && tar zxf harbor-offline-installer-{{ HARBOR_VER }}.tgz"
+
+ - name: 导入harbor所需 docker images
+ shell: "{{ bin_dir }}/docker load -i /data/harbor/harbor.{{ HARBOR_VER }}.tar.gz"
+
+ - name: 分发证书相关
+ synchronize: src={{ ca_dir }}/{{ item }} dest={{ ca_dir }}/{{ item }}
+ with_items:
+ - ca.pem
+ - ca-key.pem
+ - ca.csr
+ - ca-config.json
+ delegate_to: "{{ groups.deploy[0] }}"
+
+ - name: 创建harbor证书请求
+ template: src=harbor-csr.json.j2 dest={{ ca_dir }}/harbor-csr.json
+
+ - name: 创建harbor证书和私钥
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes harbor-csr.json | {{ bin_dir }}/cfssljson -bare harbor"
+
+ - name: 配置 harbor.cfg 文件
+ template: src=harbor-{{ HARBOR_VER_MAIN }}.cfg.j2 dest=/data/harbor/harbor.cfg
+
+ - name: 安装 harbor
+ shell: "cd /data/harbor && \
+ export PATH={{ bin_dir }}:$PATH && \
+ ./install.sh --with-clair"
+ when: '"registry" not in result.stdout'
diff --git a/roles/harbor/templates/harbor-csr.json.j2 b/roles/harbor/templates/harbor-csr.json.j2
new file mode 100644
index 0000000..1247a2b
--- /dev/null
+++ b/roles/harbor/templates/harbor-csr.json.j2
@@ -0,0 +1,21 @@
+{
+ "CN": "harbor",
+ "hosts": [
+ "127.0.0.1",
+ "{{ inventory_hostname }}",
+ "{{ HARBOR_DOMAIN }}"
+ ],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/harbor/templates/harbor-v1.5.cfg.j2 b/roles/harbor/templates/harbor-v1.5.cfg.j2
new file mode 100644
index 0000000..8cfb09f
--- /dev/null
+++ b/roles/harbor/templates/harbor-v1.5.cfg.j2
@@ -0,0 +1,181 @@
+## Configuration file of Harbor
+
+#This attribute is for migrator to detect the version of the .cfg file, DO NOT MODIFY!
+_version = 1.5.0
+#The IP address or hostname to access admin UI and registry service.
+#DO NOT use localhost or 127.0.0.1, because Harbor needs to be accessed by external clients.
+hostname = {{ inventory_hostname }}
+
+#The protocol for accessing the UI and token/notification service, by default it is http.
+#It can be set to https if ssl is enabled on nginx.
+ui_url_protocol = https
+
+#Maximum number of job workers in job service
+max_job_workers = 50
+
+#Determine whether or not to generate certificate for the registry's token.
+#If the value is on, the prepare script creates new root cert and private key
+#for generating token to access the registry. If the value is off the default key/cert will be used.
+#This flag also controls the creation of the notary signer's cert.
+customize_crt = on
+
+#The path of cert and key files for nginx, they are applied only the protocol is set to https
+ssl_cert = {{ ca_dir }}/harbor.pem
+ssl_cert_key = {{ ca_dir }}/harbor-key.pem
+
+#The path of secretkey storage
+secretkey_path = /data
+
+#Admiral's url, comment this attribute, or set its value to NA when Harbor is standalone
+admiral_url = NA
+
+#Log files are rotated log_rotate_count times before being removed. If count is 0, old versions are removed rather than rotated.
+log_rotate_count = 50
+#Log files are rotated only if they grow bigger than log_rotate_size bytes. If size is followed by k, the size is assumed to be in kilobytes.
+#If the M is used, the size is in megabytes, and if G is used, the size is in gigabytes. So size 100, size 100k, size 100M and size 100G
+#are all valid.
+log_rotate_size = 200M
+
+#Config http proxy for Clair, e.g. http://my.proxy.com:3128
+#Clair doesn't need to connect to harbor ui container via http proxy.
+http_proxy =
+https_proxy =
+no_proxy = 127.0.0.1,localhost,ui
+
+#NOTES: The properties between BEGIN INITIAL PROPERTIES and END INITIAL PROPERTIES
+#only take effect in the first boot, the subsequent changes of these properties
+#should be performed on web ui
+
+#************************BEGIN INITIAL PROPERTIES************************
+
+#Email account settings for sending out password resetting emails.
+
+#Email server uses the given username and password to authenticate on TLS connections to host and act as identity.
+#Identity left blank to act as username.
+email_identity =
+
+email_server = smtp.mydomain.com
+email_server_port = 25
+email_username = sample_admin@mydomain.com
+email_password = abc
+email_from = admin
+email_ssl = false
+email_insecure = false
+
+##The initial password of Harbor admin, only works for the first time when Harbor starts.
+#It has no effect after the first launch of Harbor.
+#Change the admin password from UI after launching Harbor.
+harbor_admin_password = Harbor12345
+
+##By default the auth mode is db_auth, i.e. the credentials are stored in a local database.
+#Set it to ldap_auth if you want to verify a user's credentials against an LDAP server.
+auth_mode = db_auth
+
+#The url for an ldap endpoint.
+ldap_url = ldaps://ldap.mydomain.com
+
+#A user's DN who has the permission to search the LDAP/AD server.
+#If your LDAP/AD server does not support anonymous search, you should configure this DN and ldap_search_pwd.
+#ldap_searchdn = uid=searchuser,ou=people,dc=mydomain,dc=com
+
+#the password of the ldap_searchdn
+#ldap_search_pwd = password
+
+#The base DN from which to look up a user in LDAP/AD
+ldap_basedn = ou=people,dc=mydomain,dc=com
+
+#Search filter for LDAP/AD, make sure the syntax of the filter is correct.
+#ldap_filter = (objectClass=person)
+
+# The attribute used in a search to match a user, it could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD
+ldap_uid = uid
+
+#the scope to search for users, 0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE
+ldap_scope = 2
+
+#Timeout (in seconds) when connecting to an LDAP Server. The default value (and most reasonable) is 5 seconds.
+ldap_timeout = 5
+
+#Verify certificate from LDAP server
+ldap_verify_cert = true
+
+#The base dn from which to lookup a group in LDAP/AD
+ldap_group_basedn = ou=group,dc=mydomain,dc=com
+
+#filter to search LDAP/AD group
+ldap_group_filter = objectclass=group
+
+#The attribute used to name a LDAP/AD group, it could be cn, name
+ldap_group_gid = cn
+
+#The scope to search for ldap groups. 0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE
+ldap_group_scope = 2
+
+#Turn on or off the self-registration feature
+self_registration = on
+
+#The expiration time (in minute) of token created by token service, default is 30 minutes
+token_expiration = 30
+
+#The flag to control what users have permission to create projects
+#The default value "everyone" allows everyone to creates a project.
+#Set to "adminonly" so that only admin user can create project.
+project_creation_restriction = everyone
+
+#************************END INITIAL PROPERTIES************************
+
+#######Harbor DB configuration section#######
+
+#The address of the Harbor database. Only need to change when using external db.
+db_host = mysql
+
+#The password for the root user of Harbor DB. Change this before any production use.
+db_password = root123
+
+#The port of Harbor database host
+db_port = 3306
+
+#The user name of Harbor database
+db_user = root
+
+##### End of Harbor DB configuration#######
+
+#The redis server address. Only needed in HA installation.
+#address:port[,weight,password,db_index]
+redis_url = redis:6379
+
+##########Clair DB configuration############
+
+#Clair DB host address. Only change it when using an exteral DB.
+clair_db_host = postgres
+
+#The password of the Clair's postgres database. Only effective when Harbor is deployed with Clair.
+#Please update it before deployment. Subsequent update will cause Clair's API server and Harbor unable to access Clair's database.
+clair_db_password = password
+
+#Clair DB connect port
+clair_db_port = 5432
+
+#Clair DB username
+clair_db_username = postgres
+
+#Clair default database
+clair_db = postgres
+
+##########End of Clair DB configuration############
+
+#The following attributes only need to be set when auth mode is uaa_auth
+uaa_endpoint = uaa.mydomain.org
+uaa_clientid = id
+uaa_clientsecret = secret
+uaa_verify_cert = true
+uaa_ca_cert = /path/to/ca.pem
+
+
+### Docker Registry setting ###
+#registry_storage_provider can be: filesystem, s3, gcs, azure, etc.
+registry_storage_provider_name = filesystem
+#registry_storage_provider_config is a comma separated "key: value" pairs, e.g. "key1: value, key2: value2".
+#Refer to https://docs.docker.com/registry/configuration/#storage for all available configuration.
+registry_storage_provider_config =
+
diff --git a/roles/harbor/templates/harbor-v1.6.cfg.j2 b/roles/harbor/templates/harbor-v1.6.cfg.j2
new file mode 100644
index 0000000..ad5eb17
--- /dev/null
+++ b/roles/harbor/templates/harbor-v1.6.cfg.j2
@@ -0,0 +1,203 @@
+## Configuration file of Harbor
+
+#This attribute is for migrator to detect the version of the .cfg file, DO NOT MODIFY!
+_version = 1.6.0
+#The IP address or hostname to access admin UI and registry service.
+#DO NOT use localhost or 127.0.0.1, because Harbor needs to be accessed by external clients.
+hostname = {{ inventory_hostname }}
+
+#The protocol for accessing the UI and token/notification service, by default it is http.
+#It can be set to https if ssl is enabled on nginx.
+ui_url_protocol = https
+
+#Maximum number of job workers in job service
+max_job_workers = 10
+
+#Determine whether or not to generate certificate for the registry's token.
+#If the value is on, the prepare script creates new root cert and private key
+#for generating token to access the registry. If the value is off the default key/cert will be used.
+#This flag also controls the creation of the notary signer's cert.
+customize_crt = on
+
+#The path of cert and key files for nginx, they are applied only the protocol is set to https
+ssl_cert = {{ ca_dir }}/harbor.pem
+ssl_cert_key = {{ ca_dir }}/harbor-key.pem
+
+#The path of secretkey storage
+secretkey_path = /data
+
+#Admiral's url, comment this attribute, or set its value to NA when Harbor is standalone
+admiral_url = NA
+
+#Log files are rotated log_rotate_count times before being removed. If count is 0, old versions are removed rather than rotated.
+log_rotate_count = 50
+#Log files are rotated only if they grow bigger than log_rotate_size bytes. If size is followed by k, the size is assumed to be in kilobytes.
+#If the M is used, the size is in megabytes, and if G is used, the size is in gigabytes. So size 100, size 100k, size 100M and size 100G
+#are all valid.
+log_rotate_size = 200M
+
+#Config http proxy for Clair, e.g. http://my.proxy.com:3128
+#Clair doesn't need to connect to harbor ui container via http proxy.
+http_proxy =
+https_proxy =
+no_proxy = 127.0.0.1,localhost,ui,registry
+
+#NOTES: The properties between BEGIN INITIAL PROPERTIES and END INITIAL PROPERTIES
+#only take effect in the first boot, the subsequent changes of these properties
+#should be performed on web ui
+
+#************************BEGIN INITIAL PROPERTIES************************
+
+#Email account settings for sending out password resetting emails.
+
+#Email server uses the given username and password to authenticate on TLS connections to host and act as identity.
+#Identity left blank to act as username.
+email_identity =
+
+email_server = smtp.mydomain.com
+email_server_port = 25
+email_username = sample_admin@mydomain.com
+email_password = abc
+email_from = admin
+email_ssl = false
+email_insecure = false
+
+##The initial password of Harbor admin, only works for the first time when Harbor starts.
+#It has no effect after the first launch of Harbor.
+#Change the admin password from UI after launching Harbor.
+harbor_admin_password = Harbor12345
+
+##By default the auth mode is db_auth, i.e. the credentials are stored in a local database.
+#Set it to ldap_auth if you want to verify a user's credentials against an LDAP server.
+auth_mode = db_auth
+
+#The url for an ldap endpoint.
+ldap_url = ldaps://ldap.mydomain.com
+
+#A user's DN who has the permission to search the LDAP/AD server.
+#If your LDAP/AD server does not support anonymous search, you should configure this DN and ldap_search_pwd.
+#ldap_searchdn = uid=searchuser,ou=people,dc=mydomain,dc=com
+
+#the password of the ldap_searchdn
+#ldap_search_pwd = password
+
+#The base DN from which to look up a user in LDAP/AD
+ldap_basedn = ou=people,dc=mydomain,dc=com
+
+#Search filter for LDAP/AD, make sure the syntax of the filter is correct.
+#ldap_filter = (objectClass=person)
+
+# The attribute used in a search to match a user, it could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD
+ldap_uid = uid
+
+#the scope to search for users, 0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE
+ldap_scope = 2
+
+#Timeout (in seconds) when connecting to an LDAP Server. The default value (and most reasonable) is 5 seconds.
+ldap_timeout = 5
+
+#Verify certificate from LDAP server
+ldap_verify_cert = true
+
+#The base dn from which to lookup a group in LDAP/AD
+ldap_group_basedn = ou=group,dc=mydomain,dc=com
+
+#filter to search LDAP/AD group
+ldap_group_filter = objectclass=group
+
+#The attribute used to name a LDAP/AD group, it could be cn, name
+ldap_group_gid = cn
+
+#The scope to search for ldap groups. 0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE
+ldap_group_scope = 2
+
+#Turn on or off the self-registration feature
+self_registration = off
+
+#The expiration time (in minute) of token created by token service, default is 30 minutes
+token_expiration = 30
+
+#The flag to control what users have permission to create projects
+#The default value "everyone" allows everyone to creates a project.
+#Set to "adminonly" so that only admin user can create project.
+project_creation_restriction = adminonly
+
+#************************END INITIAL PROPERTIES************************
+
+#######Harbor DB configuration section#######
+
+#The address of the Harbor database. Only need to change when using external db.
+db_host = postgresql
+
+#The password for the root user of Harbor DB. Change this before any production use.
+db_password = root123
+
+#The port of Harbor database host
+db_port = 5432
+
+#The user name of Harbor database
+db_user = postgres
+
+##### End of Harbor DB configuration#######
+
+##########Redis server configuration.############
+
+#Redis connection address
+redis_host = redis
+
+#Redis connection port
+redis_port = 6379
+
+#Redis connection password
+redis_password =
+
+#Redis connection db index
+#db_index 1,2,3 is for registry, jobservice and chartmuseum.
+#db_index 0 is for UI, it's unchangeable
+redis_db_index = 1,2,3
+
+##########Redis server configuration.############
+
+##########Clair DB configuration############
+
+#Clair DB host address. Only change it when using an exteral DB.
+clair_db_host = postgresql
+#The password of the Clair's postgres database. Only effective when Harbor is deployed with Clair.
+#Please update it before deployment. Subsequent update will cause Clair's API server and Harbor unable to access Clair's database.
+clair_db_password = root123
+#Clair DB connect port
+clair_db_port = 5432
+#Clair DB username
+clair_db_username = postgres
+#Clair default database
+clair_db = postgres
+
+#The interval of clair updaters, the unit is hour, set to 0 to disable the updaters.
+clair_updaters_interval = 12
+
+##########End of Clair DB configuration############
+
+#The following attributes only need to be set when auth mode is uaa_auth
+uaa_endpoint = uaa.mydomain.org
+uaa_clientid = id
+uaa_clientsecret = secret
+uaa_verify_cert = true
+uaa_ca_cert = /path/to/ca.pem
+
+
+### Harbor Storage settings ###
+#Please be aware that the following storage settings will be applied to both docker registry and helm chart repository.
+#registry_storage_provider can be: filesystem, s3, gcs, azure, etc.
+registry_storage_provider_name = filesystem
+#registry_storage_provider_config is a comma separated "key: value" pairs, e.g. "key1: value, key2: value2".
+#To avoid duplicated configurations, both docker registry and chart repository follow the same storage configuration specifications of docker registry.
+#Refer to https://docs.docker.com/registry/configuration/#storage for all available configuration.
+registry_storage_provider_config =
+#registry_custom_ca_bundle is the path to the custom root ca certificate, which will be injected into the truststore
+#of registry's and chart repository's containers. This is usually needed when the user hosts a internal storage with self signed certificate.
+registry_custom_ca_bundle =
+
+#If reload_config=true, all settings which present in harbor.cfg take effect after prepare and restart harbor, it overwrites exsiting settings.
+#reload_config=true
+#Regular expression to match skipped environment variables
+#skip_reload_env_pattern=(^EMAIL.*)|(^LDAP.*)
diff --git a/roles/helm/defaults/main.yml b/roles/helm/defaults/main.yml
new file mode 100644
index 0000000..890e8b4
--- /dev/null
+++ b/roles/helm/defaults/main.yml
@@ -0,0 +1,8 @@
+helm_namespace: kube-system
+helm_cert_cn: helm001
+tiller_sa: tiller
+tiller_cert_cn: tiller001
+tiller_image: jmgao1983/tiller:v2.12.3
+repo_url: https://kubernetes-charts.storage.googleapis.com
+# 如果默认官方repo 网络访问不稳定可以使用如下的阿里云镜像repo
+#repo_url: https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
diff --git a/roles/helm/helm.yml b/roles/helm/helm.yml
new file mode 100644
index 0000000..7965c45
--- /dev/null
+++ b/roles/helm/helm.yml
@@ -0,0 +1,3 @@
+- hosts: deploy
+ roles:
+ - helm
diff --git a/roles/helm/tasks/main.yml b/roles/helm/tasks/main.yml
new file mode 100755
index 0000000..4489c4f
--- /dev/null
+++ b/roles/helm/tasks/main.yml
@@ -0,0 +1,63 @@
+- name: 下载helm客户端
+ copy: src={{ base_dir }}/bin/helm dest={{ bin_dir }}/helm mode=0755
+
+- name: 创建helm 客户端证书请求
+ template: src=helm-csr.json.j2 dest={{ ca_dir }}/{{ helm_cert_cn }}-csr.json
+
+- name: 创建helm 客户端证书
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes {{ helm_cert_cn }}-csr.json | {{ bin_dir }}/cfssljson -bare {{ helm_cert_cn }}"
+
+- name: 创建tiller 服务端证书请求
+ template: src=tiller-csr.json.j2 dest={{ ca_dir }}/{{ tiller_cert_cn }}-csr.json
+
+- name: 创建tiller 服务端证书和私钥
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes {{ tiller_cert_cn }}-csr.json | {{ bin_dir }}/cfssljson -bare {{ tiller_cert_cn }}"
+
+- name: 准备rbac配置
+ template: src=helm-rbac.yaml.j2 dest=./helm-rbac.yaml
+
+- name: 在k8s上创建rbac
+ shell: "{{ bin_dir }}/kubectl apply -f ./helm-rbac.yaml"
+ ignore_errors: true
+
+- name: 安装tiller
+ shell: "{{ bin_dir }}/helm init \
+ --tiller-tls \
+ --tiller-tls-verify \
+ --tiller-tls-cert {{ ca_dir }}/{{ tiller_cert_cn }}.pem \
+ --tiller-tls-key {{ ca_dir }}/{{ tiller_cert_cn }}-key.pem \
+ --tls-ca-cert {{ ca_dir }}/ca.pem \
+ --service-account {{ tiller_sa }} \
+ --tiller-namespace {{ helm_namespace }} \
+ --tiller-image {{ tiller_image }} \
+ --stable-repo-url {{ repo_url }}"
+ ignore_errors: true
+
+- name: 配置helm客户端
+ shell: "cp -f {{ ca_dir }}/ca.pem ~/.helm/ca.pem && \
+ cp -f {{ ca_dir }}/{{ helm_cert_cn }}.pem ~/.helm/cert.pem && \
+ cp -f {{ ca_dir }}/{{ helm_cert_cn }}-key.pem ~/.helm/key.pem"
+ ignore_errors: true
+
+- name: 添加 helm 命令自动补全
+ lineinfile:
+ dest: ~/.bashrc
+ state: present
+ regexp: 'helm completion'
+ line: 'source <(helm completion bash)'
+
+# 为方便与tiller进行安全通信,启用helm tls环境变量;仅支持helm v2.11.0及以上版本
+- name: 配置helm tls环境变量
+ lineinfile:
+ dest: ~/.bashrc
+ state: present
+ regexp: "helm tls environment"
+ line: "export HELM_TLS_ENABLE=true"
diff --git a/roles/helm/templates/helm-csr.json.j2 b/roles/helm/templates/helm-csr.json.j2
new file mode 100644
index 0000000..94b590b
--- /dev/null
+++ b/roles/helm/templates/helm-csr.json.j2
@@ -0,0 +1,17 @@
+{
+ "CN": "{{ helm_cert_cn }}",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/helm/templates/helm-rbac.yaml.j2 b/roles/helm/templates/helm-rbac.yaml.j2
new file mode 100644
index 0000000..b15bcd5
--- /dev/null
+++ b/roles/helm/templates/helm-rbac.yaml.j2
@@ -0,0 +1,26 @@
+# 绑定helm sa到 cluster-admin,这样可以兼容现有需要集群特权的charts
+#
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: {{ helm_namespace }}
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ tiller_sa }}
+ namespace: {{ helm_namespace }}
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: tiller-cb
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+subjects:
+ - kind: ServiceAccount
+ name: {{ tiller_sa }}
+ namespace: {{ helm_namespace }}
diff --git a/roles/helm/templates/strict-helm-rbac.yaml.j2 b/roles/helm/templates/strict-helm-rbac.yaml.j2
new file mode 100644
index 0000000..549288d
--- /dev/null
+++ b/roles/helm/templates/strict-helm-rbac.yaml.j2
@@ -0,0 +1,62 @@
+# 限制helm应用只允许部署在指定namespace
+# 可以配合NetworkPolicy等实现namespace间网络完全隔离
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: {{ helm_namespace }}
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ tiller_sa }}
+ namespace: {{ helm_namespace }}
+---
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: tiller-manager
+ namespace: {{ helm_namespace }}
+rules:
+- apiGroups: ["", "extensions", "apps"]
+ resources: ["*"]
+ verbs: ["*"]
+---
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: tiller-binding
+ namespace: {{ helm_namespace }}
+subjects:
+- kind: ServiceAccount
+ name: {{ tiller_sa }}
+ namespace: {{ helm_namespace }}
+roleRef:
+ kind: Role
+ name: tiller-manager
+ apiGroup: rbac.authorization.k8s.io
+---
+#
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: tiller-cluster-manager
+rules:
+- apiGroups: ["rbac.authorization.k8s.io"]
+ resources:
+ - clusterroles
+ - clusterrolebindings
+ verbs: ["*"]
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: tiller-cluster-binding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: tiller-cluster-manager
+subjects:
+- kind: ServiceAccount
+ name: {{ tiller_sa }}
+ namespace: {{ helm_namespace }}
diff --git a/roles/helm/templates/tiller-csr.json.j2 b/roles/helm/templates/tiller-csr.json.j2
new file mode 100644
index 0000000..6d715b7
--- /dev/null
+++ b/roles/helm/templates/tiller-csr.json.j2
@@ -0,0 +1,17 @@
+{
+ "CN": "{{ tiller_cert_cn }}",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/kube-master/defaults/main.yml b/roles/kube-master/defaults/main.yml
new file mode 100644
index 0000000..a8d775a
--- /dev/null
+++ b/roles/kube-master/defaults/main.yml
@@ -0,0 +1,10 @@
+# etcd 集群服务地址列表, 根据etcd组成员自动生成
+TMP_ENDPOINTS: "{% for h in groups['etcd'] %}https://{{ h }}:2379,{% endfor %}"
+ETCD_ENDPOINTS: "{{ TMP_ENDPOINTS.rstrip(',') }}"
+
+# k8s 集群 master 节点证书配置,可以添加多个ip和域名(比如增加公网ip和域名)
+MASTER_CERT_HOSTS:
+ - "10.1.1.1"
+ - "k8s.test.io"
+ #- "61.182.11.41"
+ #- "www.test.com"
diff --git a/roles/kube-master/tasks/main.yml b/roles/kube-master/tasks/main.yml
new file mode 100644
index 0000000..e813f65
--- /dev/null
+++ b/roles/kube-master/tasks/main.yml
@@ -0,0 +1,89 @@
+- name: 下载 kube-master 二进制
+ copy: src={{ base_dir }}/bin/{{ item }} dest={{ bin_dir }}/{{ item }} mode=0755
+ with_items:
+ - kube-apiserver
+ - kube-controller-manager
+ - kube-scheduler
+ tags: upgrade_k8s
+
+- name: 分发证书相关
+ synchronize: src={{ ca_dir }}/{{ item }} dest={{ ca_dir }}/{{ item }}
+ with_items:
+ - admin.pem
+ - admin-key.pem
+ - ca.pem
+ - ca-key.pem
+ - ca.csr
+ - ca-config.json
+ delegate_to: "{{ groups.deploy[0] }}"
+
+- name: 创建 kubernetes 证书签名请求
+ template: src=kubernetes-csr.json.j2 dest={{ ca_dir }}/kubernetes-csr.json
+ tags: change_cert
+
+- name: 创建 kubernetes 证书和私钥
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes kubernetes-csr.json | {{ bin_dir }}/cfssljson -bare kubernetes"
+ tags: change_cert
+
+# 创建aggregator proxy相关证书
+- name: 创建 aggregator proxy证书签名请求
+ template: src=aggregator-proxy-csr.json.j2 dest={{ ca_dir }}/aggregator-proxy-csr.json
+ tags: upgrade_k8s
+
+- name: 创建 aggregator-proxy证书和私钥
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes aggregator-proxy-csr.json | {{ bin_dir }}/cfssljson -bare aggregator-proxy"
+ tags: upgrade_k8s
+
+- name: 创建 basic-auth.csv
+ template: src=basic-auth.csv.j2 dest={{ ca_dir }}/basic-auth.csv
+
+# 为兼容v1.8版本,配置不同 kube-apiserver的systemd unit文件
+- name: 获取 k8s 版本信息
+ shell: "{{ bin_dir }}/kube-apiserver --version"
+ register: k8s_ver
+ tags: upgrade_k8s, restart_master
+
+- name: 创建kube-apiserver的systemd unit文件
+ template: src=kube-apiserver.service.j2 dest=/etc/systemd/system/kube-apiserver.service
+ tags: upgrade_k8s, restart_master
+ when: "'v1.8' not in k8s_ver.stdout"
+
+- name: 创建kube-apiserver v1.8的systemd unit文件
+ template: src=kube-apiserver-v1.8.service.j2 dest=/etc/systemd/system/kube-apiserver.service
+ tags: upgrade_k8s, restart_master
+ when: "'v1.8' in k8s_ver.stdout"
+
+- name: 创建kube-controller-manager的systemd unit文件
+ template: src=kube-controller-manager.service.j2 dest=/etc/systemd/system/kube-controller-manager.service
+ tags: upgrade_k8s, restart_master
+
+- name: 创建kube-scheduler的systemd unit文件
+ template: src=kube-scheduler.service.j2 dest=/etc/systemd/system/kube-scheduler.service
+ tags: upgrade_k8s, restart_master
+
+- name: enable master 服务
+ shell: systemctl enable kube-apiserver kube-controller-manager kube-scheduler
+ ignore_errors: true
+
+- name: 启动 master 服务
+ shell: "systemctl daemon-reload && systemctl restart kube-apiserver && \
+ systemctl restart kube-controller-manager && systemctl restart kube-scheduler"
+ tags: upgrade_k8s, restart_master
+
+- name: 以轮询的方式等待master服务启动完成
+ command: "{{ bin_dir }}/kubectl get node"
+ register: result
+ until: result.rc == 0
+ retries: 5
+ delay: 6
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: True
+ tags: upgrade_k8s, restart_master
diff --git a/roles/kube-master/templates/aggregator-proxy-csr.json.j2 b/roles/kube-master/templates/aggregator-proxy-csr.json.j2
new file mode 100644
index 0000000..b78d687
--- /dev/null
+++ b/roles/kube-master/templates/aggregator-proxy-csr.json.j2
@@ -0,0 +1,17 @@
+{
+ "CN": "aggregator",
+ "hosts": [],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/kube-master/templates/basic-auth.csv.j2 b/roles/kube-master/templates/basic-auth.csv.j2
new file mode 100644
index 0000000..223b053
--- /dev/null
+++ b/roles/kube-master/templates/basic-auth.csv.j2
@@ -0,0 +1,2 @@
+{{ BASIC_AUTH_PASS }},{{ BASIC_AUTH_USER }},1
+readonly,readonly,2
diff --git a/roles/kube-master/templates/kube-apiserver-v1.8.service.j2 b/roles/kube-master/templates/kube-apiserver-v1.8.service.j2
new file mode 100644
index 0000000..0c3cbbb
--- /dev/null
+++ b/roles/kube-master/templates/kube-apiserver-v1.8.service.j2
@@ -0,0 +1,51 @@
+[Unit]
+Description=Kubernetes API Server
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+After=network.target
+
+[Service]
+ExecStart={{ bin_dir }}/kube-apiserver \
+ --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota,NodeRestriction \
+ --bind-address={{ inventory_hostname }} \
+ --insecure-bind-address=127.0.0.1 \
+ --authorization-mode=Node,RBAC \
+ --kubelet-https=true \
+ --kubelet-client-certificate={{ ca_dir }}/admin.pem \
+ --kubelet-client-key={{ ca_dir }}/admin-key.pem \
+ --anonymous-auth=false \
+ --basic-auth-file={{ ca_dir }}/basic-auth.csv \
+ --service-cluster-ip-range={{ SERVICE_CIDR }} \
+ --service-node-port-range={{ NODE_PORT_RANGE }} \
+ --tls-cert-file={{ ca_dir }}/kubernetes.pem \
+ --tls-private-key-file={{ ca_dir }}/kubernetes-key.pem \
+ --client-ca-file={{ ca_dir }}/ca.pem \
+ --service-account-key-file={{ ca_dir }}/ca-key.pem \
+ --etcd-cafile={{ ca_dir }}/ca.pem \
+ --etcd-certfile={{ ca_dir }}/kubernetes.pem \
+ --etcd-keyfile={{ ca_dir }}/kubernetes-key.pem \
+ --etcd-servers={{ ETCD_ENDPOINTS }} \
+ --enable-swagger-ui=true \
+ --apiserver-count={% if DEPLOY_MODE == "multi-master" %}{{ groups['kube-master']|length }}{% else %}1{% endif %} \
+ --allow-privileged=true \
+ --audit-log-maxage=30 \
+ --audit-log-maxbackup=3 \
+ --audit-log-maxsize=100 \
+ --audit-log-path=/var/lib/audit.log \
+ --event-ttl=1h \
+ --requestheader-client-ca-file={{ ca_dir }}/ca.pem \
+ --requestheader-allowed-names= \
+ --requestheader-extra-headers-prefix=X-Remote-Extra- \
+ --requestheader-group-headers=X-Remote-Group \
+ --requestheader-username-headers=X-Remote-User \
+ --proxy-client-cert-file={{ ca_dir }}/aggregator-proxy.pem \
+ --proxy-client-key-file={{ ca_dir }}/aggregator-proxy-key.pem \
+ --enable-aggregator-routing=true \
+ --runtime-config=batch/v2alpha1=true \
+ --v=2
+Restart=on-failure
+RestartSec=5
+Type=notify
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/kube-master/templates/kube-apiserver.service.j2 b/roles/kube-master/templates/kube-apiserver.service.j2
new file mode 100644
index 0000000..dd13fe2
--- /dev/null
+++ b/roles/kube-master/templates/kube-apiserver.service.j2
@@ -0,0 +1,51 @@
+[Unit]
+Description=Kubernetes API Server
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+After=network.target
+
+[Service]
+ExecStart={{ bin_dir }}/kube-apiserver \
+ --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook \
+ --bind-address={{ inventory_hostname }} \
+ --insecure-bind-address=127.0.0.1 \
+ --authorization-mode=Node,RBAC \
+ --kubelet-https=true \
+ --kubelet-client-certificate={{ ca_dir }}/admin.pem \
+ --kubelet-client-key={{ ca_dir }}/admin-key.pem \
+ --anonymous-auth=false \
+ --basic-auth-file={{ ca_dir }}/basic-auth.csv \
+ --service-cluster-ip-range={{ SERVICE_CIDR }} \
+ --service-node-port-range={{ NODE_PORT_RANGE }} \
+ --tls-cert-file={{ ca_dir }}/kubernetes.pem \
+ --tls-private-key-file={{ ca_dir }}/kubernetes-key.pem \
+ --client-ca-file={{ ca_dir }}/ca.pem \
+ --service-account-key-file={{ ca_dir }}/ca-key.pem \
+ --etcd-cafile={{ ca_dir }}/ca.pem \
+ --etcd-certfile={{ ca_dir }}/kubernetes.pem \
+ --etcd-keyfile={{ ca_dir }}/kubernetes-key.pem \
+ --etcd-servers={{ ETCD_ENDPOINTS }} \
+ --enable-swagger-ui=true \
+ --endpoint-reconciler-type=lease \
+ --allow-privileged=true \
+ --audit-log-maxage=30 \
+ --audit-log-maxbackup=3 \
+ --audit-log-maxsize=100 \
+ --audit-log-path=/var/lib/audit.log \
+ --event-ttl=1h \
+ --requestheader-client-ca-file={{ ca_dir }}/ca.pem \
+ --requestheader-allowed-names= \
+ --requestheader-extra-headers-prefix=X-Remote-Extra- \
+ --requestheader-group-headers=X-Remote-Group \
+ --requestheader-username-headers=X-Remote-User \
+ --proxy-client-cert-file={{ ca_dir }}/aggregator-proxy.pem \
+ --proxy-client-key-file={{ ca_dir }}/aggregator-proxy-key.pem \
+ --enable-aggregator-routing=true \
+ --runtime-config=batch/v2alpha1=true \
+ --v=2
+Restart=on-failure
+RestartSec=5
+Type=notify
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/kube-master/templates/kube-controller-manager.service.j2 b/roles/kube-master/templates/kube-controller-manager.service.j2
new file mode 100644
index 0000000..3e2151d
--- /dev/null
+++ b/roles/kube-master/templates/kube-controller-manager.service.j2
@@ -0,0 +1,24 @@
+[Unit]
+Description=Kubernetes Controller Manager
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+
+[Service]
+ExecStart={{ bin_dir }}/kube-controller-manager \
+ --address=127.0.0.1 \
+ --master=http://127.0.0.1:8080 \
+ --allocate-node-cidrs=true \
+ --service-cluster-ip-range={{ SERVICE_CIDR }} \
+ --cluster-cidr={{ CLUSTER_CIDR }} \
+ --cluster-name=kubernetes \
+ --cluster-signing-cert-file={{ ca_dir }}/ca.pem \
+ --cluster-signing-key-file={{ ca_dir }}/ca-key.pem \
+ --service-account-private-key-file={{ ca_dir }}/ca-key.pem \
+ --root-ca-file={{ ca_dir }}/ca.pem \
+ --horizontal-pod-autoscaler-use-rest-clients=true \
+ --leader-elect=true \
+ --v=2
+Restart=on-failure
+RestartSec=5
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/kube-master/templates/kube-scheduler.service.j2 b/roles/kube-master/templates/kube-scheduler.service.j2
new file mode 100644
index 0000000..c94cb34
--- /dev/null
+++ b/roles/kube-master/templates/kube-scheduler.service.j2
@@ -0,0 +1,15 @@
+[Unit]
+Description=Kubernetes Scheduler
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+
+[Service]
+ExecStart={{ bin_dir }}/kube-scheduler \
+ --address=127.0.0.1 \
+ --master=http://127.0.0.1:8080 \
+ --leader-elect=true \
+ --v=2
+Restart=on-failure
+RestartSec=5
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/kube-master/templates/kubernetes-csr.json.j2 b/roles/kube-master/templates/kubernetes-csr.json.j2
new file mode 100644
index 0000000..6886db4
--- /dev/null
+++ b/roles/kube-master/templates/kubernetes-csr.json.j2
@@ -0,0 +1,30 @@
+{
+ "CN": "kubernetes",
+ "hosts": [
+ "127.0.0.1",
+ "{{ MASTER_IP }}",
+ "{{ inventory_hostname }}",
+ "{{ CLUSTER_KUBERNETES_SVC_IP }}",
+{% for HOST in MASTER_CERT_HOSTS %}
+ "{{ HOST }}",
+{% endfor %}
+ "kubernetes",
+ "kubernetes.default",
+ "kubernetes.default.svc",
+ "kubernetes.default.svc.cluster",
+ "kubernetes.default.svc.cluster.local"
+ ],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "k8s",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/kube-node/defaults/main.yml b/roles/kube-node/defaults/main.yml
new file mode 100644
index 0000000..86252af
--- /dev/null
+++ b/roles/kube-node/defaults/main.yml
@@ -0,0 +1,12 @@
+# 默认使用kube-proxy的 'iptables' 模式,可选 'ipvs' 模式(experimental)
+PROXY_MODE: "iptables"
+
+# 基础容器镜像
+SANDBOX_IMAGE: "mirrorgooglecontainers/pause-amd64:3.1"
+#SANDBOX_IMAGE: "registry.access.redhat.com/rhel7/pod-infrastructure:latest"
+
+# Kubelet 根目录
+KUBELET_ROOT_DIR: "/var/lib/kubelet"
+
+# node节点最大pod 数
+MAX_PODS: 110
diff --git a/roles/kube-node/tasks/main.yml b/roles/kube-node/tasks/main.yml
new file mode 100644
index 0000000..f0ee7f9
--- /dev/null
+++ b/roles/kube-node/tasks/main.yml
@@ -0,0 +1,145 @@
+# 创建kubelet,kube-proxy工作目录和cni配置目录
+- name: 创建kube-node 相关目录
+ file: name={{ item }} state=directory
+ with_items:
+ - /var/lib/kubelet
+ - /var/lib/kube-proxy
+ - /etc/cni/net.d
+ - /root/.kube
+
+- name: 下载 kubelet,kube-proxy 二进制和基础 cni plugins
+ copy: src={{ base_dir }}/bin/{{ item }} dest={{ bin_dir }}/{{ item }} mode=0755
+ with_items:
+ - kubectl
+ - kubelet
+ - kube-proxy
+ - bridge
+ - host-local
+ - loopback
+ tags: upgrade_k8s
+
+- name: 分发 kubeconfig配置文件
+ synchronize: src=/root/.kube/config dest=/root/.kube/config
+ delegate_to: "{{ groups.deploy[0] }}"
+
+- name: 添加 kubectl 命令自动补全
+ lineinfile:
+ dest: ~/.bashrc
+ state: present
+ regexp: 'kubectl completion'
+ line: 'source <(kubectl completion bash)'
+
+- name: 分发证书相关
+ synchronize: src={{ ca_dir }}/{{ item }} dest={{ ca_dir }}/{{ item }}
+ with_items:
+ - ca.pem
+ - ca-key.pem
+ - ca.csr
+ - ca-config.json
+ delegate_to: "{{ groups.deploy[0] }}"
+
+##----------kubelet 配置部分--------------
+- name: 准备kubelet 证书签名请求
+ template: src=kubelet-csr.json.j2 dest={{ ca_dir }}/kubelet-csr.json
+
+- name: 创建 kubelet 证书与私钥
+ shell: "cd {{ ca_dir }} && {{ bin_dir }}/cfssl gencert \
+ -ca={{ ca_dir }}/ca.pem \
+ -ca-key={{ ca_dir }}/ca-key.pem \
+ -config={{ ca_dir }}/ca-config.json \
+ -profile=kubernetes kubelet-csr.json | {{ bin_dir }}/cfssljson -bare kubelet"
+
+# 创建kubelet.kubeconfig
+- name: 设置集群参数
+ shell: "{{ bin_dir }}/kubectl config set-cluster kubernetes \
+ --certificate-authority={{ ca_dir }}/ca.pem \
+ --embed-certs=true \
+ --server={{ KUBE_APISERVER }} \
+ --kubeconfig=kubelet.kubeconfig"
+
+- name: 设置客户端认证参数
+ shell: "{{ bin_dir }}/kubectl config set-credentials system:node:{{ inventory_hostname }} \
+ --client-certificate={{ ca_dir }}/kubelet.pem \
+ --embed-certs=true \
+ --client-key={{ ca_dir }}/kubelet-key.pem \
+ --kubeconfig=kubelet.kubeconfig"
+
+- name: 设置上下文参数
+ shell: "{{ bin_dir }}/kubectl config set-context default \
+ --cluster=kubernetes \
+ --user=system:node:{{ inventory_hostname }} \
+ --kubeconfig=kubelet.kubeconfig"
+
+- name: 选择默认上下文
+ shell: "{{ bin_dir }}/kubectl config use-context default \
+ --kubeconfig=kubelet.kubeconfig"
+
+- name: 移动 kubelet.kubeconfig
+ shell: "mv /root/kubelet.kubeconfig /etc/kubernetes/"
+
+- name: 准备 cni配置文件
+ template: src=cni-default.conf.j2 dest=/etc/cni/net.d/10-default.conf
+
+- name: 创建kubelet的systemd unit文件
+ template: src=kubelet.service.j2 dest=/etc/systemd/system/kubelet.service
+ tags: upgrade_k8s, restart_node
+
+- name: 开机启用kubelet 服务
+ shell: systemctl enable kubelet
+ ignore_errors: true
+
+- name: 开启kubelet 服务
+ shell: systemctl daemon-reload && systemctl restart kubelet
+ tags: upgrade_k8s, restart_node
+
+##-------kube-proxy部分----------------
+- name: 安装kube-proxy.kubeconfig配置文件
+ synchronize: src=/etc/kubernetes/kube-proxy.kubeconfig dest=/etc/kubernetes/kube-proxy.kubeconfig
+ delegate_to: "{{ groups.deploy[0] }}"
+
+- name: 创建kube-proxy 服务文件
+ tags: reload-kube-proxy, upgrade_k8s, restart_node
+ template: src=kube-proxy.service.j2 dest=/etc/systemd/system/kube-proxy.service
+
+- name: 开机启用kube-proxy 服务
+ shell: systemctl enable kube-proxy
+ ignore_errors: true
+
+- name: 开启kube-proxy 服务
+ shell: systemctl daemon-reload && systemctl restart kube-proxy
+ tags: reload-kube-proxy, upgrade_k8s, restart_node
+
+# 批准 node 节点,首先轮询等待kubelet启动完成
+- name: 轮询等待kubelet启动
+ shell: "systemctl status kubelet.service|grep Active"
+ register: kubelet_status
+ until: '"running" in kubelet_status.stdout'
+ retries: 8
+ delay: 2
+
+#- name: 获取csr 请求信息
+# shell: "sleep 3 && {{ bin_dir }}/kubectl get csr"
+# delegate_to: "{{ groups.deploy[0] }}"
+# register: csr_info
+# run_once: true
+
+#- name: approve-kubelet-csr
+# shell: "{{ bin_dir }}/kubectl get csr|grep 'Pending' | awk 'NR>0{print $1}'| \
+# xargs {{ bin_dir }}/kubectl certificate approve"
+# when: '"Pending" in csr_info.stdout'
+# delegate_to: "{{ groups.deploy[0] }}"
+# run_once: true
+
+- name: 轮询等待node达到Ready状态
+ shell: "{{ bin_dir }}/kubectl get node {{ inventory_hostname }}|awk 'NR>1{print $2}'"
+ register: node_status
+ delegate_to: "{{ groups.deploy[0] }}"
+ until: node_status.stdout == "Ready" or node_status.stdout == "Ready,SchedulingDisabled"
+ retries: 8
+ delay: 8
+ tags: upgrade_k8s, restart_node
+
+- name: 设置node节点role
+ shell: "{{ bin_dir }}/kubectl label node {{ inventory_hostname }} kubernetes.io/role=node --overwrite"
+ ignore_errors: true
+ delegate_to: "{{ groups.deploy[0] }}"
diff --git a/roles/kube-node/templates/cni-default.conf.j2 b/roles/kube-node/templates/cni-default.conf.j2
new file mode 100644
index 0000000..b032f56
--- /dev/null
+++ b/roles/kube-node/templates/cni-default.conf.j2
@@ -0,0 +1,12 @@
+{
+ "name": "mynet",
+ "type": "bridge",
+ "bridge": "mynet0",
+ "isDefaultGateway": true,
+ "ipMasq": true,
+ "hairpinMode": true,
+ "ipam": {
+ "type": "host-local",
+ "subnet": "{{ CLUSTER_CIDR }}"
+ }
+}
diff --git a/roles/kube-node/templates/kube-proxy.service.j2 b/roles/kube-node/templates/kube-proxy.service.j2
new file mode 100644
index 0000000..910e04d
--- /dev/null
+++ b/roles/kube-node/templates/kube-proxy.service.j2
@@ -0,0 +1,21 @@
+[Unit]
+Description=Kubernetes Kube-Proxy Server
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+After=network.target
+
+[Service]
+# kube-proxy 根据 --cluster-cidr 判断集群内部和外部流量,指定 --cluster-cidr 或 --masquerade-all 选项后
+# kube-proxy 会对访问 Service IP 的请求做 SNAT,这个特性与calico 实现 network policy冲突,因此禁用
+WorkingDirectory=/var/lib/kube-proxy
+ExecStart={{ bin_dir }}/kube-proxy \
+ --bind-address={{ inventory_hostname }} \
+ --hostname-override={{ inventory_hostname }} \
+ --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig \
+ --logtostderr=true \
+ --proxy-mode={{ PROXY_MODE }}
+Restart=on-failure
+RestartSec=5
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/kube-node/templates/kubelet-csr.json.j2 b/roles/kube-node/templates/kubelet-csr.json.j2
new file mode 100644
index 0000000..86c59ba
--- /dev/null
+++ b/roles/kube-node/templates/kubelet-csr.json.j2
@@ -0,0 +1,20 @@
+{
+ "CN": "system:node:{{ inventory_hostname }}",
+ "hosts": [
+ "127.0.0.1",
+ "{{ inventory_hostname }}"
+ ],
+ "key": {
+ "algo": "rsa",
+ "size": 2048
+ },
+ "names": [
+ {
+ "C": "CN",
+ "ST": "HangZhou",
+ "L": "XS",
+ "O": "system:nodes",
+ "OU": "System"
+ }
+ ]
+}
diff --git a/roles/kube-node/templates/kubelet.service.j2 b/roles/kube-node/templates/kubelet.service.j2
new file mode 100644
index 0000000..0677436
--- /dev/null
+++ b/roles/kube-node/templates/kubelet.service.j2
@@ -0,0 +1,41 @@
+[Unit]
+Description=Kubernetes Kubelet
+Documentation=https://github.com/GoogleCloudPlatform/kubernetes
+After=docker.service
+Requires=docker.service
+
+[Service]
+WorkingDirectory=/var/lib/kubelet
+ExecStart={{ bin_dir }}/kubelet \
+ --address={{ inventory_hostname }} \
+ --allow-privileged=true \
+ --anonymous-auth=false \
+ --authentication-token-webhook \
+ --authorization-mode=Webhook \
+ --client-ca-file={{ ca_dir }}/ca.pem \
+ --cluster-dns={{ CLUSTER_DNS_SVC_IP }} \
+ --cluster-domain={{ CLUSTER_DNS_DOMAIN }} \
+ --cni-bin-dir={{ bin_dir }} \
+ --cni-conf-dir=/etc/cni/net.d \
+ --fail-swap-on=false \
+ --hairpin-mode hairpin-veth \
+ --hostname-override={{ inventory_hostname }} \
+ --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \
+ --max-pods={{ MAX_PODS }} \
+ --network-plugin=cni \
+ --pod-infra-container-image={{ SANDBOX_IMAGE }} \
+ --register-node=true \
+ --root-dir={{ KUBELET_ROOT_DIR }} \
+ --tls-cert-file={{ ca_dir }}/kubelet.pem \
+ --tls-private-key-file={{ ca_dir }}/kubelet-key.pem \
+ --v=2
+#kubelet cAdvisor 默认在所有接口监听 4194 端口的请求, 以下iptables限制内网访问
+ExecStartPost=/sbin/iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 4194 -j ACCEPT
+ExecStartPost=/sbin/iptables -A INPUT -s 172.16.0.0/12 -p tcp --dport 4194 -j ACCEPT
+ExecStartPost=/sbin/iptables -A INPUT -s 192.168.0.0/16 -p tcp --dport 4194 -j ACCEPT
+ExecStartPost=/sbin/iptables -A INPUT -p tcp --dport 4194 -j DROP
+Restart=on-failure
+RestartSec=5
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/kube-router/defaults/main.yml b/roles/kube-router/defaults/main.yml
new file mode 100644
index 0000000..ffdfb94
--- /dev/null
+++ b/roles/kube-router/defaults/main.yml
@@ -0,0 +1,23 @@
+# 更多设置,参考https://github.com/cloudnativelabs/kube-router/blob/master/docs/user-guide.md
+
+# 如果 node 节点有多块网卡,请设置 true
+# 另外发现设置为 true 时能够解决v1.10使用ipvs偶尔出现pod内‘dial tcp 10.68.0.1:443: i/o timeout’的 bug
+NODE_WITH_MULTIPLE_NETWORKS: "true"
+
+# Router 支持开关
+ROUTER_ENABLE: "true"
+
+# NetworkPolicy 支持开关
+FIREWALL_ENABLE: "true"
+
+# service-proxy 支持开关,如选择 'false' 即使用k8s集群默认的kube-proxy
+SERVICE_PROXY: "false"
+
+# kube-router 镜像版本
+kube_router_ver: "v0.2.0"
+busybox_ver: "1.28.4"
+PullPolicy: "IfNotPresent"
+
+# kube-router 离线镜像tar包
+kuberouter_offline: "kube-router_{{ kube_router_ver }}.tar"
+busybox_offline: "busybox_{{ busybox_ver }}.tar"
diff --git a/roles/kube-router/kube-router.yml b/roles/kube-router/kube-router.yml
new file mode 100644
index 0000000..5d2fae6
--- /dev/null
+++ b/roles/kube-router/kube-router.yml
@@ -0,0 +1,5 @@
+- hosts:
+ - kube-master
+ - kube-node
+ roles:
+ - kube-router
diff --git a/roles/kube-router/tasks/main.yml b/roles/kube-router/tasks/main.yml
new file mode 100644
index 0000000..ffd2436
--- /dev/null
+++ b/roles/kube-router/tasks/main.yml
@@ -0,0 +1,89 @@
+- block:
+ - name: 在deploy 节点创建相关目录
+ file: name=/opt/kube/kube-system/kube-router state=directory
+
+ - name: 准备配置 kube-router DaemonSet (without IPVS)
+ template: src=kuberouter.yaml.j2 dest=/opt/kube/kube-system/kube-router/kuberouter.yaml
+ when: 'SERVICE_PROXY != "true"'
+
+ - name: 准备配置 kube-router DaemonSet (with IPVS)
+ template: src=kuberouter-all.yaml.j2 dest=/opt/kube/kube-system/kube-router/kuberouter.yaml
+ when: 'SERVICE_PROXY == "true"'
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+- name: 检查是否已下载离线kube-router镜像
+ command: "ls {{ base_dir }}/down"
+ register: download_info
+ connection: local
+ run_once: true
+
+- name: 创建cni 和kube-router 相关目录
+ file: name={{ item }} state=directory
+ with_items:
+ - /etc/cni/net.d
+ - /opt/kube/images
+
+- name: 下载cni plugins
+ copy: src={{ base_dir }}/bin/{{ item }} dest={{ bin_dir }}/{{ item }} mode=0755
+ with_items:
+ - bridge
+ - host-local
+ - loopback
+ - portmap
+
+# kube-router 带service proxy (IPVS/LVS)
+- name: 停止 kube-proxy 服务
+ service: name=kube-proxy state=stopped enabled=no
+ when: 'SERVICE_PROXY == "true"'
+ ignore_errors: true
+
+- name: 清理 kube-proxy产生的iptables或ipvs规则
+ shell: "{{ bin_dir }}/kube-proxy --cleanup; ipvsadm -C"
+ when: 'SERVICE_PROXY == "true"'
+ ignore_errors: true
+
+# 【可选】推送离线docker 镜像,可以忽略执行错误
+- name: 尝试推送离线docker 镜像(若执行失败,可忽略)
+ copy: src={{ base_dir }}/down/{{ item }} dest=/opt/kube/images/{{ item }}
+ when: 'item in download_info.stdout'
+ with_items:
+ - "pause_3.1.tar"
+ - "{{ kuberouter_offline }}"
+ - "{{ busybox_offline }}"
+ ignore_errors: true
+
+- name: 获取kube-router离线镜像推送情况
+ command: "ls /opt/kube/images"
+ register: image_info
+
+# 如果目录下有离线镜像,就把它导入到node节点上
+- name: 导入 kube-router的离线镜像(若执行失败,可忽略)
+ shell: "{{ bin_dir }}/docker load -i /opt/kube/images/{{ item }}"
+ when: 'item in image_info.stdout'
+ with_items:
+ - "pause_3.1.tar"
+ - "{{ kuberouter_offline }}"
+ - "{{ busybox_offline }}"
+ ignore_errors: true
+
+# 只需单节点执行一次
+- name: 运行 kube-router DaemonSet
+ shell: "{{ bin_dir }}/kubectl apply -f /opt/kube/kube-system/kube-router/ && sleep 5"
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+# 删除原有cni配置
+- name: 删除默认cni配置
+ file: path=/etc/cni/net.d/10-default.conf state=absent
+
+# 等待网络插件部署成功,视下载镜像速度而定
+- name: 轮询等待kube-router 运行,视下载镜像速度而定
+ shell: "{{ bin_dir }}/kubectl get pod -n kube-system -o wide|grep 'kube-router'|grep ' {{ inventory_hostname }} '|awk '{print $3}'"
+ register: pod_status
+ until: pod_status.stdout == "Running"
+ delegate_to: "{{ groups.deploy[0] }}"
+ retries: 15
+ delay: 8
+ ignore_errors: true
+
diff --git a/roles/kube-router/templates/kuberouter-all.yaml.j2 b/roles/kube-router/templates/kuberouter-all.yaml.j2
new file mode 100644
index 0000000..507e4a3
--- /dev/null
+++ b/roles/kube-router/templates/kuberouter-all.yaml.j2
@@ -0,0 +1,202 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: kube-router-cfg
+ namespace: kube-system
+ labels:
+ tier: node
+ k8s-app: kube-router
+data:
+ cni-conf.json: |
+ {
+ "name":"kubernetes",
+ "type":"bridge",
+ "bridge":"kube-bridge",
+ "isDefaultGateway":true,
+ "ipam": {
+ "type":"host-local"
+ }
+ }
+ kubeconfig: |
+ apiVersion: v1
+ kind: Config
+ clusterCIDR: "{{ CLUSTER_CIDR }}"
+ clusters:
+ - name: cluster
+ cluster:
+ certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
+ server: {{ KUBE_APISERVER }}
+ users:
+ - name: kube-router
+ user:
+ tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
+ contexts:
+ - context:
+ cluster: cluster
+ user: kube-router
+ name: kube-router-context
+ current-context: kube-router-context
+
+---
+apiVersion: extensions/v1beta1
+kind: DaemonSet
+metadata:
+ labels:
+ k8s-app: kube-router
+ tier: node
+ name: kube-router
+ namespace: kube-system
+spec:
+ template:
+ metadata:
+ labels:
+ k8s-app: kube-router
+ tier: node
+ annotations:
+ scheduler.alpha.kubernetes.io/critical-pod: ''
+ spec:
+ serviceAccountName: kube-router
+ containers:
+ - name: kube-router
+ image: cloudnativelabs/kube-router:{{ kube_router_ver }}
+ imagePullPolicy: {{ PullPolicy }}
+ args:
+ - "--run-router=true"
+ - "--run-firewall={{ FIREWALL_ENABLE }}"
+ - "--run-service-proxy=true"
+ - "--kubeconfig=/var/lib/kube-router/kubeconfig"
+ env:
+ - name: NODE_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.nodeName
+{% if NODE_WITH_MULTIPLE_NETWORKS == 'true' %}
+ # if hosts have multiple net interfaces, set following two ENVs
+ - name: KUBERNETES_SERVICE_HOST
+ value: "{{ MASTER_IP }}"
+ #value: "{{ KUBE_APISERVER.split(':')[1].lstrip('/') }}"
+ - name: KUBERNETES_SERVICE_PORT
+ value: "{{ KUBE_APISERVER.split(':')[2] }}"
+{% endif %}
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 20244
+ initialDelaySeconds: 10
+ periodSeconds: 3
+ resources:
+ requests:
+ cpu: 250m
+ memory: 250Mi
+ securityContext:
+ privileged: true
+ volumeMounts:
+ - name: lib-modules
+ mountPath: /lib/modules
+ readOnly: true
+ - name: cni-conf-dir
+ mountPath: /etc/cni/net.d
+ - name: kubeconfig
+ mountPath: /var/lib/kube-router
+ readOnly: true
+ initContainers:
+ - name: install-cni
+ image: busybox:{{ busybox_ver }}
+ imagePullPolicy: {{ PullPolicy }}
+ command:
+ - /bin/sh
+ - -c
+ - set -e -x;
+ if [ ! -f /etc/cni/net.d/10-kuberouter.conf ]; then
+ TMP=/etc/cni/net.d/.tmp-kuberouter-cfg;
+ cp /etc/kube-router/cni-conf.json ${TMP};
+ mv ${TMP} /etc/cni/net.d/10-kuberouter.conf;
+ fi;
+ if [ ! -f /var/lib/kube-router/kubeconfig ]; then
+ TMP=/var/lib/kube-router/.tmp-kubeconfig;
+ cp /etc/kube-router/kubeconfig ${TMP};
+ mv ${TMP} /var/lib/kube-router/kubeconfig;
+ fi
+ volumeMounts:
+ - mountPath: /etc/cni/net.d
+ name: cni-conf-dir
+ - mountPath: /etc/kube-router
+ name: kube-router-cfg
+ - name: kubeconfig
+ mountPath: /var/lib/kube-router
+ hostNetwork: true
+ tolerations:
+ - key: CriticalAddonsOnly
+ operator: Exists
+ - effect: NoSchedule
+ key: node-role.kubernetes.io/master
+ operator: Exists
+ volumes:
+ - name: lib-modules
+ hostPath:
+ path: /lib/modules
+ - name: cni-conf-dir
+ hostPath:
+ path: /etc/cni/net.d
+ - name: kube-router-cfg
+ configMap:
+ name: kube-router-cfg
+ - name: kubeconfig
+ hostPath:
+ path: /var/lib/kube-router
+
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: kube-router
+ namespace: kube-system
+
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: kube-router
+ namespace: kube-system
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - namespaces
+ - pods
+ - services
+ - nodes
+ - endpoints
+ verbs:
+ - list
+ - get
+ - watch
+ - apiGroups:
+ - "networking.k8s.io"
+ resources:
+ - networkpolicies
+ verbs:
+ - list
+ - get
+ - watch
+ - apiGroups:
+ - extensions
+ resources:
+ - networkpolicies
+ verbs:
+ - get
+ - list
+ - watch
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: kube-router
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: kube-router
+subjects:
+- kind: ServiceAccount
+ name: kube-router
+ namespace: kube-system
diff --git a/roles/kube-router/templates/kuberouter.yaml.j2 b/roles/kube-router/templates/kuberouter.yaml.j2
new file mode 100644
index 0000000..a9c10a9
--- /dev/null
+++ b/roles/kube-router/templates/kuberouter.yaml.j2
@@ -0,0 +1,170 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: kube-router-cfg
+ namespace: kube-system
+ labels:
+ tier: node
+ k8s-app: kube-router
+data:
+ cni-conf.json: |
+ {
+ "name":"kubernetes",
+ "type":"bridge",
+ "bridge":"kube-bridge",
+ "isDefaultGateway":true,
+ "ipam": {
+ "type":"host-local"
+ }
+ }
+
+---
+apiVersion: extensions/v1beta1
+kind: DaemonSet
+metadata:
+ labels:
+ k8s-app: kube-router
+ tier: node
+ name: kube-router
+ namespace: kube-system
+spec:
+ template:
+ metadata:
+ labels:
+ k8s-app: kube-router
+ tier: node
+ annotations:
+ scheduler.alpha.kubernetes.io/critical-pod: ''
+ spec:
+ serviceAccountName: kube-router
+ containers:
+ - name: kube-router
+ image: cloudnativelabs/kube-router:{{ kube_router_ver }}
+ imagePullPolicy: {{ PullPolicy }}
+ args:
+ - "--run-router=true"
+ - "--run-firewall={{ FIREWALL_ENABLE }}"
+ - "--run-service-proxy=false"
+ env:
+ - name: NODE_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: spec.nodeName
+{% if NODE_WITH_MULTIPLE_NETWORKS == 'true' %}
+ # if hosts have multiple net interfaces, set following two ENVs
+ - name: KUBERNETES_SERVICE_HOST
+ value: "{{ MASTER_IP }}"
+ #value: "{{ KUBE_APISERVER.split(':')[1].lstrip('/') }}"
+ - name: KUBERNETES_SERVICE_PORT
+ value: "{{ KUBE_APISERVER.split(':')[2] }}"
+{% endif %}
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 20244
+ initialDelaySeconds: 10
+ periodSeconds: 3
+ resources:
+ requests:
+ cpu: 250m
+ memory: 250Mi
+ securityContext:
+ privileged: true
+ volumeMounts:
+ - name: lib-modules
+ mountPath: /lib/modules
+ readOnly: true
+ - name: cni-conf-dir
+ mountPath: /etc/cni/net.d
+ initContainers:
+ - name: install-cni
+ image: busybox:{{ busybox_ver }}
+ imagePullPolicy: {{ PullPolicy }}
+ command:
+ - /bin/sh
+ - -c
+ - set -e -x;
+ if [ ! -f /etc/cni/net.d/10-kuberouter.conf ]; then
+ TMP=/etc/cni/net.d/.tmp-kuberouter-cfg;
+ cp /etc/kube-router/cni-conf.json ${TMP};
+ mv ${TMP} /etc/cni/net.d/10-kuberouter.conf;
+ fi
+ volumeMounts:
+ - mountPath: /etc/cni/net.d
+ name: cni-conf-dir
+ - mountPath: /etc/kube-router
+ name: kube-router-cfg
+ hostNetwork: true
+ tolerations:
+ - key: CriticalAddonsOnly
+ operator: Exists
+ - effect: NoSchedule
+ key: node-role.kubernetes.io/master
+ operator: Exists
+ volumes:
+ - name: lib-modules
+ hostPath:
+ path: /lib/modules
+ - name: cni-conf-dir
+ hostPath:
+ path: /etc/cni/net.d
+ - name: kube-router-cfg
+ configMap:
+ name: kube-router-cfg
+
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: kube-router
+ namespace: kube-system
+
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: kube-router
+ namespace: kube-system
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - namespaces
+ - pods
+ - services
+ - nodes
+ - endpoints
+ verbs:
+ - list
+ - get
+ - watch
+ - apiGroups:
+ - "networking.k8s.io"
+ resources:
+ - networkpolicies
+ verbs:
+ - list
+ - get
+ - watch
+ - apiGroups:
+ - extensions
+ resources:
+ - networkpolicies
+ verbs:
+ - get
+ - list
+ - watch
+
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: kube-router
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: kube-router
+subjects:
+- kind: ServiceAccount
+ name: kube-router
+ namespace: kube-system
diff --git a/roles/lb/defaults/main.yml b/roles/lb/defaults/main.yml
new file mode 100644
index 0000000..ecef738
--- /dev/null
+++ b/roles/lb/defaults/main.yml
@@ -0,0 +1,15 @@
+# 区分多个instance的VRRP组播,同网段不能重复,取值在0-255之间
+# 因项目已设置vrrp报文单播模式,所以这个ROUTER_ID 即便同网段里面有重复也没关系
+ROUTER_ID: 111
+
+# haproxy负载均衡算法,常见如下:
+# "roundrobin": 基于服务器权重的轮询
+# "leastconn": 基于服务器最小连接数
+# "source": 基于请求源IP地址
+# "uri": 基于请求的URI
+BALANCE_ALG: "roundrobin"
+
+# 启用 ingress NodePort服务的负载均衡 (yes/no)
+INGRESS_NODEPORT_LB: "no"
+# 启用 ingress tls NodePort服务的负载均衡 (yes/no)
+INGRESS_TLS_NODEPORT_LB: "no"
diff --git a/roles/lb/lb.yml b/roles/lb/lb.yml
new file mode 100644
index 0000000..e621f85
--- /dev/null
+++ b/roles/lb/lb.yml
@@ -0,0 +1,4 @@
+- hosts: lb
+ roles:
+ - lb
+
diff --git a/roles/lb/tasks/main.yml b/roles/lb/tasks/main.yml
new file mode 100644
index 0000000..659e9b2
--- /dev/null
+++ b/roles/lb/tasks/main.yml
@@ -0,0 +1,64 @@
+# 自动设置LB节点变量'LB_IF'
+- name: 注册变量 LB_IF_TMP
+ shell: "ip a|grep '{{ inventory_hostname }}/'|awk '{print $NF}'"
+ register: LB_IF_TMP
+ tags: restart_lb
+
+- name: 设置变量 LB_IF
+ set_fact: LB_IF="{{ LB_IF_TMP.stdout }}"
+ tags: restart_lb
+
+- name: apt更新缓存刷新
+ apt: update_cache=yes cache_valid_time=72000
+ when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version|int >= 16
+
+- name: 安装 haproxy
+ package: name=haproxy state=latest
+
+- name: 创建haproxy配置目录
+ file: name=/etc/haproxy state=directory
+
+- name: 修改centos的haproxy.service
+ template: src=haproxy.service.j2 dest=/usr/lib/systemd/system/haproxy.service
+ when: (ansible_distribution == "CentOS" or ansible_distribution == "RedHat") and ansible_distribution_major_version == "7"
+ tags: restart_lb
+
+- name: 配置 haproxy
+ template: src=haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg
+ tags: restart_lb
+
+- name: 安装 keepalived
+ package: name=keepalived state=latest
+
+- name: 创建keepalived配置目录
+ file: name=/etc/keepalived state=directory
+
+- name: 配置 keepalived 主节点
+ template: src=keepalived-master.conf.j2 dest=/etc/keepalived/keepalived.conf
+ when: LB_ROLE == "master"
+ tags: restart_lb
+
+- name: 配置 keepalived 备节点
+ template: src=keepalived-backup.conf.j2 dest=/etc/keepalived/keepalived.conf
+ when: LB_ROLE == "backup"
+ tags: restart_lb
+
+- name: daemon-reload for haproxy.service
+ shell: systemctl daemon-reload
+ tags: restart_lb
+
+- name: 开机启用haproxy服务
+ shell: systemctl enable haproxy
+ ignore_errors: true
+
+- name: 重启haproxy服务
+ shell: systemctl restart haproxy
+ tags: restart_lb
+
+- name: 开机启用keepalived服务
+ shell: systemctl enable keepalived
+ ignore_errors: true
+
+- name: 重启keepalived服务
+ shell: systemctl restart keepalived
+ tags: restart_lb
diff --git a/roles/lb/templates/haproxy.cfg.j2 b/roles/lb/templates/haproxy.cfg.j2
new file mode 100644
index 0000000..b9b3409
--- /dev/null
+++ b/roles/lb/templates/haproxy.cfg.j2
@@ -0,0 +1,47 @@
+global
+ log /dev/log local0
+ log /dev/log local1 notice
+ chroot /var/lib/haproxy
+ stats socket /run/haproxy/admin.sock mode 660 level admin
+ stats timeout 30s
+ user haproxy
+ group haproxy
+ daemon
+ nbproc 1
+
+defaults
+ log global
+ timeout connect 5000
+ timeout client 10m
+ timeout server 10m
+
+listen kube-master
+ bind 0.0.0.0:{{ KUBE_APISERVER.split(':')[2] }}
+ mode tcp
+ option tcplog
+ balance {{ BALANCE_ALG }}
+{% for host in groups['kube-master'] %}
+ server {{ host }} {{ host }}:6443 check inter 2000 fall 2 rise 2 weight 1
+{% endfor %}
+{% if INGRESS_NODEPORT_LB == "yes" %}
+
+listen ingress-node
+ bind 0.0.0.0:80
+ mode tcp
+ option tcplog
+ balance {{ BALANCE_ALG }}
+{% for host in groups['kube-node'] %}
+ server {{ host }} {{ host }}:23456 check inter 2000 fall 2 rise 2 weight 1
+{% endfor %}
+{% endif %}
+{% if INGRESS_TLS_NODEPORT_LB == "yes" %}
+
+listen ingress-node-tls
+ bind 0.0.0.0:443
+ mode tcp
+ option tcplog
+ balance {{ BALANCE_ALG }}
+{% for host in groups['kube-node'] %}
+ server {{ host }} {{ host }}:23457 check inter 2000 fall 2 rise 2 weight 1
+{% endfor %}
+{% endif %}
diff --git a/roles/lb/templates/haproxy.service.j2 b/roles/lb/templates/haproxy.service.j2
new file mode 100644
index 0000000..938e1bd
--- /dev/null
+++ b/roles/lb/templates/haproxy.service.j2
@@ -0,0 +1,13 @@
+[Unit]
+Description=HAProxy Load Balancer
+After=syslog.target network.target
+
+[Service]
+EnvironmentFile=/etc/sysconfig/haproxy
+ExecStartPre=/usr/bin/mkdir -p /run/haproxy
+ExecStart=/usr/sbin/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid $OPTIONS
+ExecReload=/bin/kill -USR2 $MAINPID
+KillMode=mixed
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/lb/templates/keepalived-backup.conf.j2 b/roles/lb/templates/keepalived-backup.conf.j2
new file mode 100644
index 0000000..18512b9
--- /dev/null
+++ b/roles/lb/templates/keepalived-backup.conf.j2
@@ -0,0 +1,30 @@
+global_defs {
+ router_id lb-backup-{{ inventory_hostname }}
+}
+
+vrrp_script check-haproxy {
+ script "killall -0 haproxy"
+ interval 5
+ weight -60
+}
+
+vrrp_instance VI-kube-master {
+ state BACKUP
+ priority {{ 119 | random(61, 1) }}
+ unicast_src_ip {{ inventory_hostname }}
+ unicast_peer {
+{% for h in groups['lb'] %}{% if h != inventory_hostname %}
+ {{ h }}
+{% endif %}{% endfor %}
+ }
+ dont_track_primary
+ interface {{ LB_IF }}
+ virtual_router_id {{ ROUTER_ID }}
+ advert_int 3
+ track_script {
+ check-haproxy
+ }
+ virtual_ipaddress {
+ {{ MASTER_IP }}
+ }
+}
diff --git a/roles/lb/templates/keepalived-master.conf.j2 b/roles/lb/templates/keepalived-master.conf.j2
new file mode 100644
index 0000000..ec43ef1
--- /dev/null
+++ b/roles/lb/templates/keepalived-master.conf.j2
@@ -0,0 +1,30 @@
+global_defs {
+ router_id lb-master-{{ inventory_hostname }}
+}
+
+vrrp_script check-haproxy {
+ script "killall -0 haproxy"
+ interval 5
+ weight -60
+}
+
+vrrp_instance VI-kube-master {
+ state MASTER
+ priority 120
+ unicast_src_ip {{ inventory_hostname }}
+ unicast_peer {
+{% for h in groups['lb'] %}{% if h != inventory_hostname %}
+ {{ h }}
+{% endif %}{% endfor %}
+ }
+ dont_track_primary
+ interface {{ LB_IF }}
+ virtual_router_id {{ ROUTER_ID }}
+ advert_int 3
+ track_script {
+ check-haproxy
+ }
+ virtual_ipaddress {
+ {{ MASTER_IP }}
+ }
+}
diff --git a/roles/os-harden/README.md b/roles/os-harden/README.md
new file mode 100644
index 0000000..9e0d44e
--- /dev/null
+++ b/roles/os-harden/README.md
@@ -0,0 +1,192 @@
+# os-hardening (Ansible Role)
+
+[][1]
+[][2]
+[][3]
+
+## Description
+
+This role provides numerous security-related configurations, providing all-round base protection. It is intended to be compliant with the [DevSec Linux Baseline](https://github.com/dev-sec/linux-baseline).
+
+It configures:
+
+ * Configures package management e.g. allows only signed packages
+ * Remove packages with known issues
+ * Configures `pam` and `pam_limits` module
+ * Shadow password suite configuration
+ * Configures system path permissions
+ * Disable core dumps via soft limits
+ * Restrict Root Logins to System Console
+ * Set SUIDs
+ * Configures kernel parameters via sysctl
+
+It will not:
+
+ * Update system packages
+ * Install security patches
+
+## Requirements
+
+* Ansible 2.5.0
+
+## Warning
+
+If you're using inspec to test your machines after applying this role, please make sure to add the connecting user to the `os_ignore_users`-variable.
+Otherwise inspec will fail. For more information, see [issue #124](https://github.com/dev-sec/ansible-os-hardening/issues/124).
+
+## Variables
+
+| Name | Default Value | Description |
+| -------------- | ------------- | -----------------------------------|
+| `os_desktop_enable`| false | true if this is a desktop system, ie Xorg, KDE/GNOME/Unity/etc|
+| `os_env_extra_user_paths`| [] | add additional paths to the user's `PATH` variable (default is empty).|
+| `os_env_umask`| 027| set default permissions for new files to `750` |
+| `os_auth_pw_max_age`| 60 | maximum password age (set to `99999` to effectively disable it) |
+| `os_auth_pw_min_age`| 7 | minimum password age (before allowing any other password change)|
+| `os_auth_retries`| 5 | the maximum number of authentication attempts, before the account is locked for some time|
+| `os_auth_lockout_time`| 600 | time in seconds that needs to pass, if the account was locked due to too many failed authentication attempts|
+| `os_auth_timeout`| 60 | authentication timeout in seconds, so login will exit if this time passes|
+| `os_auth_allow_homeless`| false | true if to allow users without home to login|
+| `os_auth_pam_passwdqc_enable`| true | true if you want to use strong password checking in PAM using passwdqc|
+| `os_auth_pam_passwdqc_options`| "min=disabled,disabled,16,12,8" | set to any option line (as a string) that you want to pass to passwdqc|
+| `os_security_users_allow`| [] | list of things, that a user is allowed to do. May contain `change_user`.
+| `os_security_kernel_enable_module_loading`| true | true if you want to allowed to change kernel modules once the system is running (eg `modprobe`, `rmmod`)|
+| `os_security_kernel_enable_core_dump`| false | kernel is crashing or otherwise misbehaving and a kernel core dump is created |
+| `os_security_suid_sgid_enforce`| true | true if you want to reduce SUID/SGID bits. There is already a list of items which are searched for configured, but you can also add your own|
+| `os_security_suid_sgid_blacklist`| [] | a list of paths which should have their SUID/SGID bits removed|
+| `os_security_suid_sgid_whitelist`| [] | a list of paths which should not have their SUID/SGID bits altered|
+| `os_security_suid_sgid_remove_from_unknown`| false | true if you want to remove SUID/SGID bits from any file, that is not explicitly configured in a `blacklist`. This will make every Ansible-run search through the mounted filesystems looking for SUID/SGID bits that are not configured in the default and user blacklist. If it finds an SUID/SGID bit, it will be removed, unless this file is in your `whitelist`.|
+| `os_security_packages_clean'`| true | removes packages with known issues. See section packages.|
+| `ufw_manage_defaults` | true | true means apply all settings with `ufw_` prefix|
+| `ufw_ipt_sysctl` | '' | by default it disables IPT_SYSCTL in /etc/default/ufw. If you want to overwrite /etc/sysctl.conf values using ufw - set it to your sysctl dictionary, for example `/etc/ufw/sysctl.conf`
+| `ufw_default_input_policy` | DROP | set default input policy of ufw to `DROP` |
+| `ufw_default_output_policy` | ACCEPT | set default output policy of ufw to `ACCEPT` |
+| `ufw_default_forward_policy` | DROP| set default forward policy of ufw to `DROP` |
+
+## Packages
+
+We remove the following packages:
+
+ * xinetd ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.1)
+ * inetd ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.1)
+ * tftp-server ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.5)
+ * ypserv ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.4)
+ * telnet-server ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.2)
+ * rsh-server ([NSA](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf), Chapter 3.2.3)
+ * prelink ([open-scap](https://static.open-scap.org/ssg-guides/ssg-sl7-guide-ospp-rhel7-server.html#xccdf_org.ssgproject.content_rule_disable_prelink))
+
+## Disabled filesystems
+
+We disable the following filesystems, because they're most likely not used:
+
+ * "cramfs"
+ * "freevxfs"
+ * "jffs2"
+ * "hfs"
+ * "hfsplus"
+ * "squashfs"
+ * "udf"
+ * "vfat"
+
+To prevent some of the filesystems from being disabled, add them to the `os_filesystem_whitelist` variable.
+
+## Example Playbook
+
+ - hosts: localhost
+ roles:
+ - dev-sec.os-hardening
+
+
+## Changing sysctl variables
+If you want to override sysctl-variables, you can use the `sysctl_overwrite` variable (in older versions you had to override the whole `sysctl_dict`).
++So for example if you want to change the IPv4 traffic forwarding variable to `1`, do it like this:
+
+```
+ - hosts: localhost
+ roles:
+ - dev-sec.os-hardening
+ vars:
+ sysctl_overwrite:
+ # Enable IPv4 traffic forwarding.
+ net.ipv4.ip_forward: 1
+```
+
+Alternatively you can change Ansible's [hash-behaviour](https://docs.ansible.com/ansible/intro_configuration.html#hash-behaviour) to `merge`, then you only have to overwrite the single hash you need to. But please be aware that changing the hash-behaviour changes it for all your playbooks and is not recommended by Ansible.
+
+## Local Testing
+
+The preferred way of locally testing the role is to use Docker. You will have to install Docker on your system. See [Get started](https://docs.docker.com/) for a Docker package suitable to for your system.
+
+You can also use vagrant and Virtualbox or VMWare to run tests locally. You will have to install Virtualbox and Vagrant on your system. See [Vagrant Downloads](http://downloads.vagrantup.com/) for a vagrant package suitable for your system. For all our tests we use `test-kitchen`. If you are not familiar with `test-kitchen` please have a look at [their guide](http://kitchen.ci/docs/getting-started).
+
+Next install test-kitchen:
+
+```bash
+# Install dependencies
+gem install bundler
+bundle install
+```
+
+### Testing with Docker
+```
+# fast test on one machine
+bundle exec kitchen test default-ubuntu-1404
+
+# test on all machines
+bundle exec kitchen test
+
+# for development
+bundle exec kitchen create default-ubuntu-1404
+bundle exec kitchen converge default-ubuntu-1404
+```
+
+### Testing with Virtualbox
+```
+# fast test on one machine
+KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen test default-ubuntu-1404
+
+# test on all machines
+KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen test
+
+# for development
+KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen create default-ubuntu-1404
+KITCHEN_YAML=".kitchen.vagrant.yml" bundle exec kitchen converge default-ubuntu-1404
+```
+For more information see [test-kitchen](http://kitchen.ci/docs/getting-started)
+
+## Contributors + Kudos
+
+...
+
+This role is mostly based on guides by:
+
+* [Arch Linux wiki, Sysctl hardening](https://wiki.archlinux.org/index.php/Sysctl)
+* [NSA: Guide to the Secure Configuration of Red Hat Enterprise Linux 5](http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf)
+* [Ubuntu Security/Features](https://wiki.ubuntu.com/Security/Features)
+* [Deutsche Telekom, Group IT Security, Security Requirements (German)](https://www.telekom.com/psa)
+
+Thanks to all of you!
+## Contributing
+
+See [contributor guideline](CONTRIBUTING.md).
+
+## License and Author
+
+* Author:: Sebastian Gumprich
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+[1]: http://travis-ci.org/dev-sec/ansible-os-hardening
+[2]: https://gitter.im/dev-sec/general
+[3]: https://galaxy.ansible.com/dev-sec/os-hardening
diff --git a/roles/os-harden/defaults/main.yml b/roles/os-harden/defaults/main.yml
new file mode 100644
index 0000000..d6e8851
--- /dev/null
+++ b/roles/os-harden/defaults/main.yml
@@ -0,0 +1,249 @@
+os_desktop_enable: false
+os_env_extra_user_paths: []
+os_auth_pw_max_age: 60
+os_auth_pw_min_age: 7 # discourage password cycling
+os_auth_retries: 5
+os_auth_lockout_time: 600 # 10min
+os_auth_timeout: 60
+os_auth_allow_homeless: false
+os_auth_pam_passwdqc_enable: true
+os_auth_pam_passwdqc_options: 'min=disabled,disabled,16,12,8' # used in RHEL6
+os_auth_pam_pwquality_options: 'try_first_pass retry=3 type=' # used in RHEL7
+os_auth_root_ttys: [console, tty1, tty2, tty3, tty4, tty5, tty6]
+
+os_chfn_restrict: ''
+# may contain: change_user
+os_security_users_allow: []
+# specify system accounts those login should not be disabled and password not changed
+os_ignore_users: ['vagrant', 'kitchen']
+os_security_kernel_enable_module_loading: true
+os_security_kernel_enable_core_dump: false
+os_security_suid_sgid_enforce: true
+# user-defined blacklist and whitelist
+os_security_suid_sgid_blacklist: []
+os_security_suid_sgid_whitelist: []
+# if this is true, remove any suid/sgid bits from files that were not in the whitelist
+os_security_suid_sgid_remove_from_unknown: false
+
+# remove packages with known issues
+os_security_packages_clean: true
+os_security_packages_list: ['xinetd','inetd','ypserv','telnet-server','rsh-server', 'prelink']
+
+# Allow interactive startup (rhel, centos)
+os_security_init_prompt: true
+# Require root password for single user mode. (rhel, centos)
+os_security_init_single: false
+
+# Apply ufw defaults
+ufw_manage_defaults: true
+
+# Empty variable disables IPT_SYSCTL in /etc/default/ufw
+# by default in Ubuntu it set to: /etc/ufw/sysctl.conf
+# CAUTION
+# if you enable it - it'll overwrite /etc/sysctl.conf file, managed by hardening framework
+ufw_ipt_sysctl: ''
+
+# Default ufw variables
+ufw_default_input_policy: 'DROP'
+ufw_default_output_policy: 'ACCEPT'
+ufw_default_forward_policy: 'DROP'
+ufw_default_application_policy: 'SKIP'
+ufw_manage_builtins: 'no'
+ufw_ipt_modules: 'nf_conntrack_ftp nf_nat_ftp nf_conntrack_netbios_ns'
+
+sysctl_config:
+ # Disable IPv4 traffic forwarding. | sysctl-01
+ net.ipv4.ip_forward: 0
+
+ # Disable IPv6 traffic forwarding. | sysctl-19
+ net.ipv6.conf.all.forwarding: 0
+
+ # ignore RAs on Ipv6. | sysctl-25
+ net.ipv6.conf.all.accept_ra: 0
+ net.ipv6.conf.default.accept_ra: 0
+
+ # Enable RFC-recommended source validation feature. | sysctl-02
+ net.ipv4.conf.all.rp_filter: 1
+ net.ipv4.conf.default.rp_filter: 1
+
+ # Reduce the surface on SMURF attacks. | sysctl-04
+ # Make sure to ignore ECHO broadcasts, which are only required in broad network analysis.
+ net.ipv4.icmp_echo_ignore_broadcasts: 1
+
+ # There is no reason to accept bogus error responses from ICMP, so ignore them instead. | sysctl-03
+ net.ipv4.icmp_ignore_bogus_error_responses: 1
+
+ # Limit the amount of traffic the system uses for ICMP. | sysctl-05
+ net.ipv4.icmp_ratelimit: 100
+
+ # Adjust the ICMP ratelimit to include ping, dst unreachable,
+ # source quench, ime exceed, param problem, timestamp reply, information reply | sysctl-06
+ net.ipv4.icmp_ratemask: 88089
+
+ # Disable IPv6 | sysctl-18
+ net.ipv6.conf.all.disable_ipv6: 1
+
+ # Protect against wrapping sequence numbers at gigabit speeds | sysctl-07
+ net.ipv4.tcp_timestamps: 0
+
+ # Define restriction level for announcing the local source IP | sysctl-08
+ net.ipv4.conf.all.arp_ignore: 1
+
+ # Define mode for sending replies in response to
+ # received ARP requests that resolve local target IP addresses | sysctl-09
+ net.ipv4.conf.all.arp_announce: 2
+
+ # RFC 1337 fix F1 | sysctl-10
+ net.ipv4.tcp_rfc1337: 1
+
+ # Send(router) or accept(host) RFC1620 shared media redirects | sysctl-12
+ net.ipv4.conf.all.shared_media: 1
+ net.ipv4.conf.default.shared_media: 1
+
+ # Accepting source route can lead to malicious networking behavior,
+ # so disable it if not needed. | sysctl-13
+ net.ipv4.conf.all.accept_source_route: 0
+ net.ipv4.conf.default.accept_source_route: 0
+
+ # Accepting redirects can lead to malicious networking behavior, so disable
+ # it if not needed. | sysctl-13 | sysctl-14 | sysctl-15 | sysctl-20
+ net.ipv4.conf.default.accept_redirects: 0
+ net.ipv4.conf.all.accept_redirects: 0
+ net.ipv4.conf.all.secure_redirects: 0
+ net.ipv4.conf.default.secure_redirects: 0
+ net.ipv6.conf.default.accept_redirects: 0
+ net.ipv6.conf.all.accept_redirects: 0
+
+ # For non-routers: don't send redirects, these settings are 0 | sysctl-16
+ net.ipv4.conf.all.send_redirects: 0
+ net.ipv4.conf.default.send_redirects: 0
+
+ # log martian packets | sysctl-17
+ net.ipv4.conf.all.log_martians: 1
+ net.ipv4.conf.default.log_martians: 1
+
+ # ipv6 config
+ # Disable acceptance of IPv6 router solicitations messages | sysctl-21
+ net.ipv6.conf.default.router_solicitations: 0
+
+ # Disable Accept Router Preference from router advertisement | sysctl-22
+ net.ipv6.conf.default.accept_ra_rtr_pref: 0
+
+ # Disable learning Prefix Information from router advertisement | sysctl-23
+ net.ipv6.conf.default.accept_ra_pinfo: 0
+
+ # Disable learning Hop limit from router advertisement | sysctl-24
+ net.ipv6.conf.default.accept_ra_defrtr: 0
+
+ # Disable IPv6 autoconfiguration | sysctl-26
+ net.ipv6.conf.default.autoconf: 0
+
+ # Disable neighbor solicitations to send out per address | sysctl-27
+ net.ipv6.conf.default.dad_transmits: 0
+
+ # Assign one global unicast IPv6 addresses to each interface | sysctl-28
+ net.ipv6.conf.default.max_addresses: 1
+
+ # This settings controls how the kernel behaves towards module changes at
+ # runtime. Setting to 1 will disable module loading at runtime.
+ # Setting it to 0 is actually never supported. | sysctl-29
+ # kernel.modules_disabled: 1
+
+ # Magic Sysrq should be disabled, but can also be set to a safe value if so
+ # desired for physical machines. It can allow a safe reboot if the system hangs
+ # and is a 'cleaner' alternative to hitting the reset button. | sysctl-30
+ # The following values are permitted:
+ # * **0** - disable sysrq
+ # * **1** - enable sysrq completely
+ # * **>1** - bitmask of enabled sysrq functions:
+ # * **2** - control of console logging level
+ # * **4** - control of keyboard (SAK, unraw)
+ # * **8** - debugging dumps of processes etc.
+ # * **16** - sync command
+ # * **32** - remount read-only
+ # * **64** - signalling of processes (term, kill, oom-kill)
+ # * **128** - reboot/poweroff
+ # * **256** - nicing of all RT tasks
+ kernel.sysrq: 0
+
+ # Prevent core dumps with SUID. These are usually only
+ # needed by developers and may contain sensitive information. | sysctl-31
+ fs.suid_dumpable: 0
+
+ # Virtual memory regions protection | sysctl-32
+ kernel.randomize_va_space: 2
+
+ kernel.core_uses_pid: 1
+
+ # When an attacker is trying to exploit the local kernel, it is often
+ # helpful to be able to examine where in memory the kernel, modules,
+ # and data structures live. As such, kernel addresses should be treated
+ # as sensitive information.
+ #
+ # Many files and interfaces contain these addresses (e.g. /proc/kallsyms,
+ # /proc/modules, etc), and this setting can censor the addresses. A value
+ # of "0" allows all users to see the kernel addresses. A value of "1"
+ # limits visibility to the root user, and "2" blocks even the root user.
+ kernel.kptr_restrict: 1
+
+ # The PTRACE system is used for debugging. With it, a single user process
+ # can attach to any other dumpable process owned by the same user. In the
+ # case of malicious software, it is possible to use PTRACE to access
+ # credentials that exist in memory (re-using existing SSH connections,
+ # extracting GPG agent information, etc).
+ #
+ # A PTRACE scope of "0" is the more permissive mode. A scope of "1" limits
+ # PTRACE only to direct child processes (e.g. "gdb name-of-program" and
+ # "strace -f name-of-program" work, but gdb's "attach" and "strace -fp $PID"
+ # do not). The PTRACE scope is ignored when a user has CAP_SYS_PTRACE, so
+ # "sudo strace -fp $PID" will work as before. For more details see:
+ # https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace
+ #
+ # For applications launching crash handlers that need PTRACE, exceptions can
+ # be registered by the debugee by declaring in the segfault handler
+ # specifically which process will be using PTRACE on the debugee:
+ # prctl(PR_SET_PTRACER, debugger_pid, 0, 0, 0);
+ #
+ # In general, PTRACE is not needed for the average running Ubuntu system.
+ # To that end, the default is to set the PTRACE scope to "1". This value
+ # may not be appropriate for developers or servers with only admin accounts.
+ # kernel.yama.ptrace_scope = 1
+ kernel.yama.ptrace_scope: 1
+
+ # Protect the zero page of memory from userspace mmap to prevent kernel
+ # NULL-dereference attacks against potential future kernel security
+ # vulnerabilities. (Added in kernel 2.6.23.)
+ #
+ # While this default is built into the Ubuntu kernel, there is no way to
+ # restore the kernel default if the value is changed during runtime; for
+ # example via package removal (e.g. wine, dosemu). Therefore, this value
+ # is reset to the secure default each time the sysctl values are loaded.
+ vm.mmap_min_addr: 65536
+
+ # These settings eliminate an entire class of security vulnerability:
+ # time-of-check-time-of-use cross-privilege attacks using guessable
+ # filenames (generally seen as "/tmp file race" vulnerabilities).
+ fs.protected_hardlinks: 1
+ fs.protected_symlinks: 1
+
+# Do not delete the following line or otherwise the playbook will fail
+# at task 'create a combined sysctl-dict if overwrites are defined'
+sysctl_overwrite:
+
+# disable unused filesystems
+os_unused_filesystems:
+ - "cramfs"
+ - "freevxfs"
+ - "jffs2"
+ - "hfs"
+ - "hfsplus"
+ - "squashfs"
+ - "udf"
+ - "vfat"
+
+# whitelist for used filesystems
+os_filesystem_whitelist: []
+
+# Set to false to turn the role into a no-op. Useful when using
+# the Ansible role dependency mechanism.
+os_hardening_enabled: true
diff --git a/roles/os-harden/meta/main.yml b/roles/os-harden/meta/main.yml
new file mode 100644
index 0000000..f9ae429
--- /dev/null
+++ b/roles/os-harden/meta/main.yml
@@ -0,0 +1,27 @@
+---
+galaxy_info:
+ author: "Sebastian Gumprich"
+ description: 'This Ansible role provides numerous security-related configurations, providing all-round base protection.'
+ company: Hardening Framework Team
+ license: Apache License 2.0
+ min_ansible_version: '2.5'
+ platforms:
+ - name: EL
+ versions:
+ - 6
+ - 7
+ - name: Ubuntu
+ versions:
+ - precise
+ - trusty
+ - xenial
+ - name: Debian
+ versions:
+ - wheezy
+ - jessie
+ - name: Amazon
+ galaxy_tags:
+ - system
+ - security
+ - hardening
+dependencies: []
diff --git a/roles/os-harden/os-harden.yml b/roles/os-harden/os-harden.yml
new file mode 100644
index 0000000..a2868e8
--- /dev/null
+++ b/roles/os-harden/os-harden.yml
@@ -0,0 +1,46 @@
+# [可选]操作系统安全加固 https://github.com/dev-sec/ansible-os-hardening
+- hosts: all
+ vars:
+ os_security_users_allow: change_user
+ os_auth_pam_passwdqc_enable: false
+ os_security_suid_sgid_blacklist: ['/bin/umount']
+ os_security_suid_sgid_whitelist: ['/usr/bin/rlogin']
+ os_filesystem_whitelist: ['vfat']
+ sysctl_config:
+ net.ipv4.ip_forward: 0
+ net.ipv6.conf.all.forwarding: 0
+ net.ipv6.conf.all.accept_ra: 0
+ net.ipv6.conf.default.accept_ra: 0
+ net.ipv4.conf.all.rp_filter: 1
+ net.ipv4.conf.default.rp_filter: 1
+ net.ipv4.icmp_echo_ignore_broadcasts: 1
+ net.ipv4.icmp_ignore_bogus_error_responses: 1
+ net.ipv4.icmp_ratelimit: 100
+ net.ipv4.icmp_ratemask: 88089
+ net.ipv6.conf.all.disable_ipv6: 1
+ net.ipv4.conf.all.arp_ignore: 1
+ net.ipv4.conf.all.arp_announce: 2
+ net.ipv4.conf.all.shared_media: 1
+ net.ipv4.conf.default.shared_media: 1
+ net.ipv4.conf.all.accept_source_route: 0
+ net.ipv4.conf.default.accept_source_route: 0
+ net.ipv4.conf.default.accept_redirects: 0
+ net.ipv4.conf.all.accept_redirects: 0
+ net.ipv4.conf.all.secure_redirects: 0
+ net.ipv4.conf.default.secure_redirects: 0
+ net.ipv6.conf.default.accept_redirects: 0
+ net.ipv6.conf.all.accept_redirects: 0
+ net.ipv4.conf.all.send_redirects: 0
+ net.ipv4.conf.default.send_redirects: 0
+ net.ipv4.conf.all.log_martians: 1
+ net.ipv6.conf.default.router_solicitations: 0
+ net.ipv6.conf.default.accept_ra_rtr_pref: 0
+ net.ipv6.conf.default.accept_ra_pinfo: 0
+ net.ipv6.conf.default.accept_ra_defrtr: 0
+ net.ipv6.conf.default.autoconf: 0
+ net.ipv6.conf.default.dad_transmits: 0
+ net.ipv6.conf.default.max_addresses: 1
+ roles:
+ - os-harden
+ #- { role: os-harden, when: "OS_HARDEN is defined and OS_HARDEN == 'yes'" }
+
diff --git a/roles/os-harden/tasks/apt.yml b/roles/os-harden/tasks/apt.yml
new file mode 100644
index 0000000..b3ceff3
--- /dev/null
+++ b/roles/os-harden/tasks/apt.yml
@@ -0,0 +1,8 @@
+---
+- name: remove deprecated or insecure packages | package-01 - package-09
+ apt:
+ name: '{{ item }}'
+ state: 'absent'
+ with_items:
+ - '{{ os_security_packages_list }}'
+ when: 'os_security_packages_clean'
diff --git a/roles/os-harden/tasks/auditd.yml b/roles/os-harden/tasks/auditd.yml
new file mode 100644
index 0000000..2ff1ed2
--- /dev/null
+++ b/roles/os-harden/tasks/auditd.yml
@@ -0,0 +1,14 @@
+---
+
+- name: install auditd package | package-08
+ package:
+ name: '{{ auditd_package }}'
+ state: 'present'
+
+- name: configure auditd | package-08
+ template:
+ src: 'etc/audit/auditd.conf.j2'
+ dest: '/etc/audit/auditd.conf'
+ owner: 'root'
+ group: 'root'
+ mode: '0640'
diff --git a/roles/os-harden/tasks/find_files.yml b/roles/os-harden/tasks/find_files.yml
new file mode 100644
index 0000000..0891332
--- /dev/null
+++ b/roles/os-harden/tasks/find_files.yml
@@ -0,0 +1,12 @@
+- name: find directories for minimizing access
+ find:
+ paths: '{{ outer_item }}'
+ recurse: yes
+ register: minimize_access_directories
+
+- name: minimize access on found files
+ file:
+ path: '{{ item.path }}'
+ mode: 'go-w'
+ state: file
+ with_items: '{{ minimize_access_directories.files }}'
diff --git a/roles/os-harden/tasks/hardening.yml b/roles/os-harden/tasks/hardening.yml
new file mode 100644
index 0000000..0114980
--- /dev/null
+++ b/roles/os-harden/tasks/hardening.yml
@@ -0,0 +1,59 @@
+---
+- name: Set OS family dependent variables
+ include_vars: '{{ ansible_os_family }}.yml'
+ tags: always
+
+- name: Set OS dependent variables
+ include_vars: '{{ item }}'
+ with_first_found:
+ - files:
+ - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml'
+ - '{{ ansible_distribution }}.yml'
+ - '{{ ansible_os_family }}-{{ ansible_distribution_major_version }}.yml'
+ skip: true
+ tags: always
+
+- import_tasks: auditd.yml
+ tags: auditd
+
+- import_tasks: limits.yml
+ tags: limits
+
+- import_tasks: login_defs.yml
+ tags: login_defs
+
+- import_tasks: minimize_access.yml
+ tags: minimize_access
+
+- import_tasks: pam.yml
+ tags: pam
+
+- import_tasks: modprobe.yml
+ tags: modprobe
+
+- import_tasks: profile.yml
+ tags: profile
+
+- import_tasks: securetty.yml
+ tags: securetty
+
+- import_tasks: suid_sgid.yml
+ when: os_security_suid_sgid_enforce
+ tags: suid_sgid
+
+- import_tasks: sysctl.yml
+ tags: sysctl
+
+- import_tasks: user_accounts.yml
+ tags: user_accounts
+
+- import_tasks: rhosts.yml
+ tags: rhosts
+
+- import_tasks: yum.yml
+ when: ansible_os_family == 'RedHat'
+ tags: yum
+
+- import_tasks: apt.yml
+ when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
+ tags: apt
diff --git a/roles/os-harden/tasks/limits.yml b/roles/os-harden/tasks/limits.yml
new file mode 100644
index 0000000..ed78780
--- /dev/null
+++ b/roles/os-harden/tasks/limits.yml
@@ -0,0 +1,34 @@
+---
+
+- block:
+ - name: create limits.d-directory if it does not exist | sysctl-31a, sysctl-31b
+ file:
+ path: '/etc/security/limits.d'
+ owner: 'root'
+ group: 'root'
+ mode: '0755'
+ state: 'directory'
+
+ - name: create aditional limits config file -> 10.hardcore.conf | sysctl-31a, sysctl-31b
+ pam_limits:
+ dest: '/etc/security/limits.d/10.hardcore.conf'
+ domain: '*'
+ limit_type: hard
+ limit_item: core
+ value: 0
+ comment: Prevent core dumps for all users. These are usually only needed by developers and may contain sensitive information
+
+ - name: set 10.hardcore.conf perms to 0400 and root ownership
+ file:
+ path: /etc/security/limits.d/10.hardcore.conf
+ owner: 'root'
+ group: 'root'
+ mode: '0440'
+
+ when: 'not os_security_kernel_enable_core_dump'
+
+- name: remove 10.hardcore.conf config file
+ file:
+ path: /etc/security/limits.d/10.hardcore.conf
+ state: absent
+ when: 'os_security_kernel_enable_core_dump'
diff --git a/roles/os-harden/tasks/login_defs.yml b/roles/os-harden/tasks/login_defs.yml
new file mode 100644
index 0000000..165e615
--- /dev/null
+++ b/roles/os-harden/tasks/login_defs.yml
@@ -0,0 +1,9 @@
+---
+- name: create login.defs | os-05, os-05b
+ template:
+ src: 'etc/login.defs.j2'
+ dest: '/etc/login.defs'
+ owner: 'root'
+ group: 'root'
+ mode: '0444'
+
diff --git a/roles/os-harden/tasks/main.yml b/roles/os-harden/tasks/main.yml
new file mode 100644
index 0000000..33eb1ea
--- /dev/null
+++ b/roles/os-harden/tasks/main.yml
@@ -0,0 +1,4 @@
+---
+
+- include_tasks: hardening.yml
+ when: os_hardening_enabled
diff --git a/roles/os-harden/tasks/minimize_access.yml b/roles/os-harden/tasks/minimize_access.yml
new file mode 100644
index 0000000..fb6deb0
--- /dev/null
+++ b/roles/os-harden/tasks/minimize_access.yml
@@ -0,0 +1,35 @@
+---
+# Using a two-pass approach for checking directories in order to support symlinks.
+- include_tasks: find_files.yml
+ loop_control:
+ loop_var: outer_item
+ loop:
+ - '/usr/local/sbin'
+ - '/usr/local/bin'
+ - '/usr/sbin'
+ - '/usr/bin'
+ - '/sbin'
+ - '/bin'
+ - '{{ os_env_extra_user_paths }}'
+
+- name: change shadow ownership to root and mode to 0600 | os-02
+ file:
+ dest: '/etc/shadow'
+ owner: '{{ os_shadow_perms.owner }}'
+ group: '{{ os_shadow_perms.group }}'
+ mode: '{{ os_shadow_perms.mode }}'
+
+- name: change passwd ownership to root and mode to 0644 | os-03
+ file:
+ dest: '/etc/passwd'
+ owner: '{{ os_passwd_perms.owner }}'
+ group: '{{ os_passwd_perms.group }}'
+ mode: '{{ os_passwd_perms.mode }}'
+
+- name: change su-binary to only be accessible to user and group root
+ file:
+ dest: '/bin/su'
+ owner: 'root'
+ group: 'root'
+ mode: '0750'
+ when: os_security_users_allow != None
diff --git a/roles/os-harden/tasks/modprobe.yml b/roles/os-harden/tasks/modprobe.yml
new file mode 100644
index 0000000..98f36dc
--- /dev/null
+++ b/roles/os-harden/tasks/modprobe.yml
@@ -0,0 +1,24 @@
+---
+- name: install modprobe to disable filesystems | os-10
+ package:
+ name: '{{modprobe_package}}'
+ state: 'present'
+
+- name: check if efi is installed
+ stat:
+ path: "/sys/firmware/efi"
+ register: efi_installed
+
+- name: remove vfat from fs-list if efi is used
+ set_fact:
+ os_unused_filesystems: "{{ os_unused_filesystems | difference('vfat') }}"
+ when: efi_installed.stat.isdir is defined and efi_installed.stat.isdir
+
+- name: disable unused filesystems | os-10
+ template:
+ src: 'etc/modprobe.d/modprobe.j2'
+ dest: '/etc/modprobe.d/dev-sec.conf'
+ owner: 'root'
+ group: 'root'
+ mode: '0640'
+
diff --git a/roles/os-harden/tasks/pam.yml b/roles/os-harden/tasks/pam.yml
new file mode 100644
index 0000000..91d8e2c
--- /dev/null
+++ b/roles/os-harden/tasks/pam.yml
@@ -0,0 +1,99 @@
+---
+- name: update pam on Debian systems
+ command: 'pam-auth-update --package'
+ when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
+ changed_when: False
+ environment:
+ DEBIAN_FRONTEND: noninteractive
+
+# the reason for this is so a user cannot connect to a server,
+# that isn't connected to an LDAP server anymore.
+# normally caching credentials shouldn't be necessary for most machines.
+# removing it provides some more security while not removing usability.
+- name: remove pam ccreds to disable password caching
+ package:
+ name: '{{ os_packages_pam_ccreds }}'
+ state: 'absent'
+
+- name: remove pam_cracklib, because it does not play nice with passwdqc
+ apt:
+ name: '{{ os_packages_pam_cracklib }}'
+ state: 'absent'
+ when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and os_auth_pam_passwdqc_enable
+
+- name: install the package for strong password checking
+ apt:
+ name: '{{ os_packages_pam_passwdqc }}'
+ state: 'present'
+ update_cache: 'yes'
+ when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and os_auth_pam_passwdqc_enable
+
+- name: configure passwdqc
+ template:
+ src: 'usr/share/pam-configs/pam_passwdqd.j2'
+ dest: '{{ passwdqc_path }}'
+ mode: '0644'
+ owner: 'root'
+ group: 'root'
+ when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and os_auth_pam_passwdqc_enable
+
+- name: remove passwdqc
+ apt:
+ name: '{{ os_packages_pam_passwdqc }}'
+ state: 'absent'
+ when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and not os_auth_pam_passwdqc_enable
+
+- name: install tally2
+ apt:
+ name: 'libpam-modules'
+ state: 'present'
+ when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and not os_auth_pam_passwdqc_enable and os_auth_retries > 0
+
+- name: configure tally2
+ template:
+ src: 'usr/share/pam-configs/pam_tally2.j2'
+ dest: '{{ tally2_path }}'
+ mode: '0644'
+ owner: 'root'
+ group: 'root'
+ when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and not os_auth_pam_passwdqc_enable and os_auth_retries > 0
+
+- name: delete tally2 when retries is 0
+ file:
+ path: '{{ tally2_path }}'
+ state: 'absent'
+ when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and not os_auth_pam_passwdqc_enable and os_auth_retries == 0
+
+- name: remove pam_cracklib, because it does not play nice with passwdqc
+ yum:
+ name: '{{ os_packages_pam_cracklib }}'
+ state: 'absent'
+ when: (ansible_os_family == 'RedHat' and ansible_distribution_version < '7' and not ansible_distribution == 'Amazon') and os_auth_pam_passwdqc_enable
+
+- name: install the package for strong password checking
+ yum:
+ name: '{{ os_packages_pam_passwdqc }}'
+ state: 'present'
+ when: (ansible_os_family == 'RedHat' and ansible_distribution_version < '7' and not ansible_distribution == 'Amazon') and os_auth_pam_passwdqc_enable
+
+- name: remove passwdqc
+ yum:
+ name: '{{ os_packages_pam_passwdqc }}'
+ state: 'absent'
+ when: ansible_os_family == 'RedHat' and not os_auth_pam_passwdqc_enable
+
+- name: configure passwdqc and tally via central system-auth confic
+ template:
+ src: 'etc/pam.d/rhel_system_auth.j2'
+ dest: '/etc/pam.d/system-auth-ac'
+ mode: '0640'
+ owner: 'root'
+ group: 'root'
+
+- name: NSA 2.3.3.5 Upgrade Password Hashing Algorithm to SHA-512
+ template:
+ src: 'etc/rhel_libuser.conf.j2'
+ dest: '/etc/libuser.conf'
+ mode: '0640'
+ owner: 'root'
+ group: 'root'
diff --git a/roles/os-harden/tasks/profile.yml b/roles/os-harden/tasks/profile.yml
new file mode 100644
index 0000000..6245474
--- /dev/null
+++ b/roles/os-harden/tasks/profile.yml
@@ -0,0 +1,15 @@
+---
+- name: add pinerolo_profile.sh to profile.d
+ template:
+ src: 'etc/profile.d/profile.conf.j2'
+ dest: '/etc/profile.d/pinerolo_profile.sh'
+ owner: 'root'
+ group: 'root'
+ mode: '0750'
+ when: not os_security_kernel_enable_core_dump
+
+- name: remove pinerolo_profile.sh from profile.d
+ file:
+ path: /etc/profile.d/pinerolo_profile.sh
+ state: absent
+ when: os_security_kernel_enable_core_dump
diff --git a/roles/os-harden/tasks/rhosts.yml b/roles/os-harden/tasks/rhosts.yml
new file mode 100644
index 0000000..bad12c7
--- /dev/null
+++ b/roles/os-harden/tasks/rhosts.yml
@@ -0,0 +1,23 @@
+---
+- name: Get user accounts | os-09
+ command: "awk -F: '{print $1}' /etc/passwd"
+ changed_when: False
+ check_mode: False
+ register: users
+
+- name: delete rhosts-files from system | os-09
+ file:
+ dest: '~{{ item }}/.rhosts'
+ state: 'absent'
+ with_flattened: '{{ users.stdout_lines | default([]) }}'
+
+- name: delete hosts.equiv from system | os-01
+ file:
+ dest: '/etc/hosts.equiv'
+ state: 'absent'
+
+- name: delete .netrc-files from system | os-09
+ file:
+ dest: '~{{ item }}/.netrc'
+ state: 'absent'
+ with_flattened: '{{ users.stdout_lines | default([]) }}'
\ No newline at end of file
diff --git a/roles/os-harden/tasks/securetty.yml b/roles/os-harden/tasks/securetty.yml
new file mode 100644
index 0000000..1743a19
--- /dev/null
+++ b/roles/os-harden/tasks/securetty.yml
@@ -0,0 +1,8 @@
+---
+- name: create securetty
+ template:
+ src: 'etc/securetty.j2'
+ dest: '/etc/securetty'
+ owner: 'root'
+ group: 'root'
+ mode: '0400'
diff --git a/roles/os-harden/tasks/suid_sgid.yml b/roles/os-harden/tasks/suid_sgid.yml
new file mode 100644
index 0000000..625b7ff
--- /dev/null
+++ b/roles/os-harden/tasks/suid_sgid.yml
@@ -0,0 +1,32 @@
+---
+- name: remove suid/sgid bit from binaries in blacklist | os-06
+ file:
+ path: '{{ item }}'
+ mode: 'a-s'
+ state: 'file'
+ follow: 'yes'
+ failed_when: false
+ with_flattened:
+ - '{{ os_security_suid_sgid_system_blacklist }}'
+ - '{{ os_security_suid_sgid_blacklist }}'
+
+- name: find binaries with suid/sgid set | os-06
+ shell: find / -xdev \( -perm -4000 -o -perm -2000 \) -type f ! -path '/proc/*' -print 2>/dev/null
+ register: sbit_binaries
+ when: os_security_suid_sgid_remove_from_unknown
+ changed_when: False
+
+- name: gather files from which to remove suids/sgids and remove system white-listed files | os-06
+ set_fact:
+ suid: '{{ sbit_binaries.stdout_lines | difference(os_security_suid_sgid_system_whitelist) }}'
+ when: os_security_suid_sgid_remove_from_unknown
+
+- name: remove suid/sgid bit from all binaries except in system and user whitelist | os-06
+ file:
+ path: '{{ item }}'
+ mode: 'a-s'
+ state: 'file'
+ follow: 'yes'
+ with_flattened:
+ - '{{ suid | default([]) | difference(os_security_suid_sgid_whitelist) }}'
+ when: os_security_suid_sgid_remove_from_unknown
diff --git a/roles/os-harden/tasks/sysctl.yml b/roles/os-harden/tasks/sysctl.yml
new file mode 100644
index 0000000..5ff9719
--- /dev/null
+++ b/roles/os-harden/tasks/sysctl.yml
@@ -0,0 +1,69 @@
+---
+- name: protect sysctl.conf
+ file:
+ path: '/etc/sysctl.conf'
+ owner: 'root'
+ group: 'root'
+ mode: '0440'
+
+- name: set Daemon umask, do config for rhel-family | NSA 2.2.4.1
+ template:
+ src: 'etc/sysconfig/rhel_sysconfig_init.j2'
+ dest: '/etc/sysconfig/init'
+ owner: 'root'
+ group: 'root'
+ mode: '0544'
+ when: ansible_distribution == 'RedHat' or ansible_distribution == 'Fedora' or ansible_distribution == 'CentOS' or ansible_distribution == 'Amazon'
+
+- name: install initramfs-tools
+ apt:
+ name: 'initramfs-tools'
+ state: 'present'
+ update_cache: true
+ when: ansible_os_family == 'Debian' and os_security_kernel_enable_module_loading
+
+- name: rebuild initramfs with starting pack of modules, if module loading at runtime is disabled
+ template:
+ src: 'etc/initramfs-tools/modules.j2'
+ dest: '/etc/initramfs-tools/modules'
+ owner: 'root'
+ group: 'root'
+ mode: '0440'
+ when: ansible_os_family == 'Debian' and os_security_kernel_enable_module_loading
+ register: initramfs
+
+- name: update-initramfs
+ command: 'update-initramfs -u'
+ when: initramfs.changed
+
+- name: create a combined sysctl-dict if overwrites are defined
+ set_fact:
+ sysctl_config: '{{ sysctl_config | combine(sysctl_overwrite) }}'
+ when: sysctl_overwrite | default()
+
+- name: Change various sysctl-settings, look at the sysctl-vars file for documentation
+ sysctl:
+ name: '{{ item.key }}'
+ value: '{{ item.value }}'
+ sysctl_set: yes
+ state: present
+ reload: yes
+ ignoreerrors: yes
+ with_dict: '{{ sysctl_config }}'
+
+- name: Change various sysctl-settings on rhel6-hosts or older, look at the sysctl-vars file for documentation
+ sysctl:
+ name: '{{ item.key }}'
+ value: '{{ item.value }}'
+ state: present
+ reload: yes
+ ignoreerrors: yes
+ with_dict: '{{ sysctl_rhel_config }}'
+ when: ((ansible_distribution == 'RedHat' or ansible_distribution == 'Fedora' or ansible_distribution == 'CentOS') and ansible_distribution_major_version < '7') or ansible_distribution == 'Amazon'
+
+- name: Apply ufw defaults
+ template:
+ src: 'etc/default/ufw.j2'
+ dest: '/etc/default/ufw'
+ when: ufw_manage_defaults and (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu')
+ tags: ufw
diff --git a/roles/os-harden/tasks/user_accounts.yml b/roles/os-harden/tasks/user_accounts.yml
new file mode 100644
index 0000000..9160535
--- /dev/null
+++ b/roles/os-harden/tasks/user_accounts.yml
@@ -0,0 +1,45 @@
+---
+- name: get UID_MIN from login.defs
+ shell: awk '/^\s*UID_MIN\s*([0-9]*).*?$/ {print $2}' /etc/login.defs
+ args:
+ removes: /etc/login.defs
+ register: uid_min
+ check_mode: False
+ changed_when: False
+
+- name: calculate UID_MAX from UID_MIN by substracting 1
+ set_fact:
+ uid_max: '{{ uid_min.stdout | int - 1 }}'
+ when: uid_min is defined
+
+- name: set UID_MAX on Debian-systems if no login.defs exist
+ set_fact:
+ uid_max: '999'
+ when: ansible_os_family == 'Debian' and not uid_min
+
+- name: set UID_MAX on other systems if no login.defs exist
+ set_fact:
+ uid_max: '499'
+ when: not uid_min
+
+- name: get all system accounts
+ command: awk -F'':'' '{ if ( $3 <= {{ uid_max|quote }} ) print $1}' /etc/passwd
+ args:
+ removes: /etc/passwd
+ changed_when: False
+ check_mode: False
+ register: sys_accs
+
+- name: remove always ignored system accounts from list
+ set_fact:
+ sys_accs_cond: '{{ sys_accs.stdout_lines | difference(os_always_ignore_users) }}'
+ check_mode: False
+
+- name: change system accounts not on the user provided ignore-list
+ user:
+ name: '{{ item }}'
+ shell: '{{ os_nologin_shell_path }}'
+ password: '*'
+ createhome: False
+ with_flattened:
+ - '{{ sys_accs_cond | default([]) | difference(os_ignore_users) | list }}'
diff --git a/roles/os-harden/tasks/yum.yml b/roles/os-harden/tasks/yum.yml
new file mode 100644
index 0000000..2f4000f
--- /dev/null
+++ b/roles/os-harden/tasks/yum.yml
@@ -0,0 +1,47 @@
+---
+- name: remove unused repositories
+ file:
+ name: '/etc/yum.repos.d/{{ item }}.repo'
+ state: 'absent'
+ with_items:
+ - 'CentOS-Debuginfo'
+ - 'CentOS-Media'
+ - 'CentOS-Vault'
+ when: os_security_packages_clean
+
+- name: get yum-repository-files
+ shell: 'find /etc/yum.repos.d/ -type f -name *.repo'
+ changed_when: False
+ register: yum_repos
+
+- name: check if rhnplugin.conf exists
+ stat:
+ path: '/etc/yum/pluginconf.d/rhnplugin.conf'
+ register: rhnplugin_file
+
+ # for the 'default([])' see here:
+ # https://github.com/dev-sec/ansible-os-hardening/issues/99 and
+ # https://stackoverflow.com/questions/37067827/ansible-deprecation-warning-for-undefined-variable-despite-when-clause
+- name: activate gpg-check for yum-repos
+ replace:
+ dest: '{{ item }}'
+ regexp: '^\s*gpgcheck: 0'
+ replace: 'gpgcheck: 1'
+ with_flattened:
+ - '/etc/yum.conf'
+ - '{{ yum_repos.stdout_lines| default([]) }}'
+
+- name: activate gpg-check for yum rhn if it exists
+ replace:
+ dest: '/etc/yum/pluginconf.d/rhnplugin.conf'
+ regexp: '^\s*gpgcheck: 0'
+ replace: 'gpgcheck: 1'
+ when: rhnplugin_file.stat.exists
+
+- name: remove deprecated or insecure packages | package-01 - package-09
+ yum:
+ name: '{{ item }}'
+ state: 'absent'
+ with_items:
+ - '{{ os_security_packages_list }}'
+ when: os_security_packages_clean
diff --git a/roles/os-harden/templates/etc/audit/auditd.conf.j2 b/roles/os-harden/templates/etc/audit/auditd.conf.j2
new file mode 100644
index 0000000..4f3040f
--- /dev/null
+++ b/roles/os-harden/templates/etc/audit/auditd.conf.j2
@@ -0,0 +1,28 @@
+log_file = /var/log/audit/audit.log
+log_format = RAW
+log_group = root
+priority_boost = 4
+flush = INCREMENTAL
+freq = 20
+num_logs = 5
+disp_qos = lossy
+dispatcher = /sbin/audispd
+name_format = NONE
+##name = mydomain
+max_log_file = 6
+max_log_file_action = keep_logs
+space_left = 75
+space_left_action = SYSLOG
+action_mail_acct = root
+admin_space_left = 50
+admin_space_left_action = SUSPEND
+disk_full_action = SUSPEND
+disk_error_action = SUSPEND
+##tcp_listen_port =
+tcp_listen_queue = 5
+tcp_max_per_addr = 1
+##tcp_client_ports = 1024-65535
+tcp_client_max_idle = 0
+enable_krb5 = no
+krb5_principal = auditd
+##krb5_key_file = /etc/audit/audit.key
diff --git a/roles/os-harden/templates/etc/default/ufw.j2 b/roles/os-harden/templates/etc/default/ufw.j2
new file mode 100644
index 0000000..bf8c0b4
--- /dev/null
+++ b/roles/os-harden/templates/etc/default/ufw.j2
@@ -0,0 +1,45 @@
+# {{ ansible_managed | comment }}
+# /etc/default/ufw
+#
+
+# Set to yes to apply rules to support IPv6 (no means only IPv6 on loopback
+# accepted). You will need to 'disable' and then 'enable' the firewall for
+# the changes to take affect.
+IPV6={{ 'no' if sysctl_config['net.ipv6.conf.all.disable_ipv6'] is defined and sysctl_config['net.ipv6.conf.all.disable_ipv6'] == 1 else 'yes' }}
+
+# Set the default input policy to ACCEPT, DROP, or REJECT. Please note that if
+# you change this you will most likely want to adjust your rules.
+DEFAULT_INPUT_POLICY="{{ ufw_default_input_policy }}"
+
+# Set the default output policy to ACCEPT, DROP, or REJECT. Please note that if
+# you change this you will most likely want to adjust your rules.
+DEFAULT_OUTPUT_POLICY="{{ ufw_default_output_policy }}"
+
+# Set the default forward policy to ACCEPT, DROP or REJECT. Please note that
+# if you change this you will most likely want to adjust your rules
+DEFAULT_FORWARD_POLICY="{{ ufw_default_forward_policy }}"
+
+# Set the default application policy to ACCEPT, DROP, REJECT or SKIP. Please
+# note that setting this to ACCEPT may be a security risk. See 'man ufw' for
+# details
+DEFAULT_APPLICATION_POLICY="{{ ufw_default_application_policy }}"
+
+# By default, ufw only touches its own chains. Set this to 'yes' to have ufw
+# manage the built-in chains too. Warning: setting this to 'yes' will break
+# non-ufw managed firewall rules
+MANAGE_BUILTINS="{{ ufw_manage_builtins }}"
+
+#
+# IPT backend
+#
+# only enable if using iptables backend and want to overwrite /etc/sysctl.conf
+{% if ufw_ipt_sysctl == '' %}#{% endif %}IPT_SYSCTL={{ ufw_ipt_sysctl }}
+
+# Extra connection tracking modules to load. Complete list can be found in
+# net/netfilter/Kconfig of your kernel source. Some common modules:
+# nf_conntrack_irc, nf_nat_irc: DCC (Direct Client to Client) support
+# nf_conntrack_netbios_ns: NetBIOS (samba) client support
+# nf_conntrack_pptp, nf_nat_pptp: PPTP over stateful firewall/NAT
+# nf_conntrack_ftp, nf_nat_ftp: active FTP support
+# nf_conntrack_tftp, nf_nat_tftp: TFTP support (server side)
+IPT_MODULES="{{ ufw_ipt_modules }}"
diff --git a/roles/os-harden/templates/etc/initramfs-tools/modules.j2 b/roles/os-harden/templates/etc/initramfs-tools/modules.j2
new file mode 100644
index 0000000..c6603fb
--- /dev/null
+++ b/roles/os-harden/templates/etc/initramfs-tools/modules.j2
@@ -0,0 +1,111 @@
+# {{ ansible_managed | comment }}
+# This file contains the names of kernel modules that should be loaded at boot time, one per line. Lines beginning with "#" are ignored.
+#
+# A list of all available kernel modules kann be found with `find /lib/modules/$(uname -r)/kernel/`
+# We will sort by folder.
+
+
+# Arch
+# ----
+#
+# Modules for certains builds, contains support modules and some CPU-specific optimizations.
+
+{% if ansible_architecture == 'x86_64' %}
+# Optimize for x86_64 cryptographic features
+twofish-x86_64-3way
+twofish-x86_64
+aes-x86_64
+salsa20-x86_64
+blowfish-x86_64
+{% endif %}
+
+{% if 'amd' in ansible_processor %}
+# AMD-specific optimizations
+kvm-amd
+{% else %}
+# Intel-specific optimizations
+ghash-clmulni-intel
+aesni-intel
+kvm-intel
+{% endif %}
+
+kvm
+
+# Crypto
+# ------
+
+# Some core modules which comprise strong cryptography.
+blowfish_common
+blowfish_generic
+ctr
+cts
+lrw
+lzo
+rmd160
+rmd256
+rmd320
+serpent
+sha512_generic
+twofish_common
+twofish_generic
+xts
+zlib
+
+
+# Drivers
+# -------
+
+# Basics
+lp
+rtc
+loop
+
+# Filesystems
+ext2
+btrfs
+
+{% if os_desktop_enable %}
+# Desktop
+psmouse
+snd
+snd_ac97_codec
+snd_intel8x0
+snd_page_alloc
+snd_pcm
+snd_timer
+soundcore
+usbhid
+{% endif %}
+
+# Lib
+# ---
+xz
+
+
+# Net
+# ---
+
+# All packets needed for netfilter rules (ie iptables, ebtables).
+ip_tables
+x_tables
+iptable_filter
+iptable_nat
+
+# Targets
+ipt_LOG
+ipt_REJECT
+
+# Modules
+xt_connlimit
+xt_tcpudp
+xt_recent
+xt_limit
+xt_conntrack
+nf_conntrack
+nf_conntrack_ipv4
+nf_defrag_ipv4
+xt_state
+nf_nat
+
+# Addons
+xt_pknock
diff --git a/roles/os-harden/templates/etc/login.defs.j2 b/roles/os-harden/templates/etc/login.defs.j2
new file mode 100644
index 0000000..f2ceb32
--- /dev/null
+++ b/roles/os-harden/templates/etc/login.defs.j2
@@ -0,0 +1,211 @@
+# {{ ansible_managed | comment }}
+# Configuration control definitions for the login package.
+#
+# Three items must be defined: `MAIL_DIR`, `ENV_SUPATH`, and `ENV_PATH`. If unspecified, some arbitrary (and possibly incorrect) value will be assumed. All other items are optional - if not specified then the described action or option will be inhibited.
+#
+# Comment lines (lines beginning with `#`) and blank lines are ignored.
+#
+#-- Modified for Linux. --marekm
+
+# *REQUIRED for useradd/userdel/usermod*
+#
+# Directory where mailboxes reside, _or_ name of file, relative to the home directory. If you _do_ define `MAIL_DIR` and `MAIL_FILE`, `MAIL_DIR` takes precedence.
+# Essentially:
+#
+# * `MAIL_DIR` defines the location of users mail spool files (for mbox use) by appending the username to `MAIL_DIR` as defined below.
+# * `MAIL_FILE` defines the location of the users mail spool files as the fully-qualified filename obtained by prepending the user home directory before `$MAIL_FILE`
+#
+# *NOTE*: This is no more used for setting up users MAIL environment variable which is, starting from shadow 4.0.12-1 in Debian, entirely the job of the pam_mail PAM modules.
+#
+# See default PAM configuration files provided for login, su, etc.
+# This is a temporary situation: setting these variables will soon move to `/etc/default/useradd` and the variables will then be no more supported
+MAIL_DIR /var/mail
+#MAIL_FILE .mail
+
+# Enable logging and display of `/var/log/faillog` login failure info. This option conflicts with the `pam_tally` PAM module.
+FAILLOG_ENAB yes
+
+# Enable display of unknown usernames when login failures are recorded.
+#
+# *WARNING*: Unknown usernames may become world readable. See #290803 and #298773 for details about how this could become a security concern
+LOG_UNKFAIL_ENAB no
+
+# Enable logging of successful logins
+LOG_OK_LOGINS yes
+
+# Enable "syslog" logging of su activity - in addition to sulog file logging.
+SYSLOG_SU_ENAB yes
+
+# Enable "syslog" logging of newgrp and sg.
+SYSLOG_SG_ENAB yes
+
+# If defined, all su activity is logged to this file.
+#SULOG_FILE /var/log/sulog
+
+# If defined, file which maps tty line to `TERM` environment parameter. Each line of the file is in a format something like "vt100 tty01".
+#TTYTYPE_FILE /etc/ttytype
+
+# If defined, login failures will be logged here in a utmp format last, when invoked as lastb, will read `/var/log/btmp`, so...
+FTMP_FILE /var/log/btmp
+
+# If defined, the command name to display when running "su -". For # example, if this is defined as "su" then a "ps" will display the command is "-su". If not defined, then "ps" would display the name of the shell actually being run, e.g. something like "-sh".
+SU_NAME su
+
+# If defined, file which inhibits all the usual chatter during the login sequence. If a full pathname, then hushed mode will be enabled if the user's name or shell are found in the file. If not a full pathname, then hushed mode will be enabled if the file exists in the user's home directory.
+#HUSHLOGIN_FILE /etc/hushlogins
+HUSHLOGIN_FILE .hushlogin
+
+# *REQUIRED*: The default PATH settings, for superuser and normal users. (they are minimal, add the rest in the shell startup files)
+ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+ENV_PATH PATH=/usr/local/bin:/usr/bin:/bin{{ os_env_extra_user_paths| join (':') }}
+
+# Terminal permissions
+# --------------------
+
+# Login tty will be assigned this group ownership.
+# If you have a "write" program which is "setgid" to a special group which owns the terminals, define `TTYGROUP` to the group number and `TTYPERM` to `0620`. Otherwise leave `TTYGROUP` commented out and assign `TTYPERM` to either `622` or `600`.
+TTYGROUP tty
+
+# Login tty will be set to this permission.
+# In Debian `/usr/bin/bsd-write` or similar programs are setgid tty. However, the default and recommended value for `TTYPERM` is still `0600` to not allow anyone to write to anyone else console or terminal
+# Users can still allow other people to write them by issuing the `mesg y` command.
+TTYPERM 0600
+
+# Login conf initializations
+# --------------------------
+
+# Terminal ERASE character ('\010' = backspace). Only used on System V.
+ERASECHAR 0177
+
+# Terminal KILL character ('\025' = CTRL/U). Only used on System V.
+KILLCHAR 025
+
+# The default umask value for `pam_umask` and is used by useradd and newusers to set the mode of the new home directories.
+# If `USERGROUPS_ENAB` is set to `yes`, that will modify this `UMASK` default value for private user groups, i. e. the uid is the same as gid, and username is the same as the primary group name: for these, the user permissions will be used as group permissions, e. g. `022` will become `002`.
+# Prefix these values with `0` to get octal, `0x` to get hexadecimal.
+# `022` is the "historical" value in Debian for UMASK
+# `027`, or even `077`, could be considered better for privacy.
+UMASK {{ os_env_umask }}
+
+# Enable setting of the umask group bits to be the same as owner bits (examples: `022` -> `002`, `077` -> `007`) for non-root users, if the uid is the same as gid, and username is the same as the primary group name.
+# If set to yes, userdel will remove the user´s group if it contains no more members, and useradd will create by default a group with the name of the user.
+USERGROUPS_ENAB yes
+
+
+# Password aging controls
+# -----------------------
+
+# Maximum number of days a password may be used.
+PASS_MAX_DAYS {{ os_auth_pw_max_age }}
+
+# Minimum number of days allowed between password changes.
+PASS_MIN_DAYS {{ os_auth_pw_min_age }}
+
+# Number of days warning given before a password expires.
+PASS_WARN_AGE 7
+
+# Min/max values for automatic uid selection in useradd
+UID_MIN {{ os_auth_uid_min }}
+UID_MAX 60000
+# System accounts
+SYS_UID_MIN {{ os_auth_sys_uid_min }}
+SYS_UID_MAX {{ os_auth_sys_uid_max }}
+
+# Min/max values for automatic gid selection in groupadd
+GID_MIN {{ os_auth_gid_min }}
+GID_MAX 60000
+# System accounts
+SYS_GID_MIN {{ os_auth_sys_gid_min }}
+SYS_GID_MAX {{ os_auth_sys_gid_max }}
+
+# Max number of login retries if password is bad. This will most likely be overriden by PAM, since the default pam_unix module has it's own built in of 3 retries. However, this is a safe fallback in case you are using an authentication module that does not enforce PAM_MAXTRIES.
+LOGIN_RETRIES {{ os_auth_retries }}
+
+# Max time in seconds for login
+LOGIN_TIMEOUT {{ os_auth_timeout }}
+
+# Which fields may be changed by regular users using chfn - use any combination of letters "frwh" (full name, room number, work phone, home phone). If not defined, no changes are allowed.
+# For backward compatibility, "yes" = "rwh" and "no" = "frwh".
+{% if os_chfn_restrict %}
+CHFN_RESTRICT {{ os_chfn_restrict }}
+{% endif %}
+# Should login be allowed if we can't cd to the home directory?
+DEFAULT_HOME {{ 'yes' if os_auth_allow_homeless else 'no' }}
+
+# If defined, this command is run when removing a user.
+# It should remove any at/cron/print jobs etc. owned by
+# the user to be removed (passed as the first argument).
+#USERDEL_CMD /usr/sbin/userdel_local
+
+# Instead of the real user shell, the program specified by this parameter will be launched, although its visible name (`argv[0]`) will be the shell's. The program may do whatever it wants (logging, additional authentification, banner, ...) before running the actual shell.
+#FAKE_SHELL /bin/fakeshell
+
+# If defined, either full pathname of a file containing device names or a ":" delimited list of device names. Root logins will be allowed only upon these devices.
+# This variable is used by login and su.
+#CONSOLE /etc/consoles
+#CONSOLE console:tty01:tty02:tty03:tty04
+
+# List of groups to add to the user's supplementary group set when logging in on the console (as determined by the `CONSOLE` setting). Default is none.
+# Use with caution - it is possible for users to gain permanent access to these groups, even when not logged in on the console. How to do it is left as an exercise for the reader...
+# This variable is used by login and su.
+#CONSOLE_GROUPS floppy:audio:cdrom
+
+# If set to `MD5`, MD5-based algorithm will be used for encrypting password
+# If set to `SHA256`, SHA256-based algorithm will be used for encrypting password
+# If set to `SHA512`, SHA512-based algorithm will be used for encrypting password
+# If set to `DES`, DES-based algorithm will be used for encrypting password (default)
+# Overrides the MD5_CRYPT_ENAB option
+#
+# Note: It is recommended to use a value consistent with
+# the PAM modules configuration.
+MD5_CRYPT_ENAB no
+ENCRYPT_METHOD SHA512
+
+# Only used if `ENCRYPT_METHOD` is set to `SHA256` or `SHA512`: Define the number of SHA rounds.
+# With a lot of rounds, it is more difficult to brute forcing the password. But note also that it more CPU resources will be needed to authenticate users.
+# If not specified, the libc will choose the default number of rounds (5000). The values must be inside the 1000-999999999 range. If only one of the MIN or MAX values is set, then this value will be used.
+# If MIN > MAX, the highest value will be used.
+#SHA_CRYPT_MIN_ROUNDS 5000
+#SHA_CRYPT_MAX_ROUNDS 5000
+
+
+# Obsoleted by PAM
+# ================
+# These options are now handled by PAM. Please edit the appropriate file in `/etc/pam.d/` to enable the equivelants of them.
+#MOTD_FILE
+#DIALUPS_CHECK_ENAB
+#LASTLOG_ENAB
+#MAIL_CHECK_ENAB
+#OBSCURE_CHECKS_ENAB
+#PORTTIME_CHECKS_ENAB
+#SU_WHEEL_ONLY
+#CRACKLIB_DICTPATH
+#PASS_CHANGE_TRIES
+#PASS_ALWAYS_WARN
+#ENVIRON_FILE
+#NOLOGINS_FILE
+#ISSUE_FILE
+#PASS_MIN_LEN
+#PASS_MAX_LEN
+#ULIMIT
+#ENV_HZ
+#CHFN_AUTH
+#CHSH_AUTH
+#FAIL_DELAY
+
+# Obsoleted
+# =========
+# These options are no more handled by shadow.
+# Shadow utilities will display a warning if they still appear.
+#CLOSE_SESSIONS
+#LOGIN_STRING
+#NO_PASSWORD_CONSOLE
+#QMAIL_DIR
+
+# If set to `yes`, new passwords will be encrypted using the MD5-based algorithm compatible with the one used by recent releases of FreeBSD. It supports passwords of unlimited length and longer salt strings.
+# Set to `no` if you need to copy encrypted passwords to other systems which don't understand the new algorithm. Default is `no`.
+# This variable is deprecated. You should use ENCRYPT_METHOD.
+#
+#MD5_CRYPT_ENAB no
+
+
diff --git a/roles/os-harden/templates/etc/modprobe.d/modprobe.j2 b/roles/os-harden/templates/etc/modprobe.d/modprobe.j2
new file mode 100644
index 0000000..ca8c20c
--- /dev/null
+++ b/roles/os-harden/templates/etc/modprobe.d/modprobe.j2
@@ -0,0 +1,5 @@
+{{ ansible_managed | comment }}
+
+{% for fs in os_unused_filesystems | difference(os_filesystem_whitelist) %}
+install {{fs}} /bin/true
+{% endfor %}
diff --git a/roles/os-harden/templates/etc/pam.d/rhel_system_auth.j2 b/roles/os-harden/templates/etc/pam.d/rhel_system_auth.j2
new file mode 100644
index 0000000..d84b438
--- /dev/null
+++ b/roles/os-harden/templates/etc/pam.d/rhel_system_auth.j2
@@ -0,0 +1,39 @@
+# {{ ansible_managed | comment }}
+
+#%PAM-1.0
+{% if os_auth_retries > 0 %}
+auth required pam_tally2.so deny={{ os_auth_retries }} onerr=fail unlock_time={{ os_auth_lockout_time }}
+{% endif %}
+auth required pam_env.so
+auth sufficient pam_unix.so nullok try_first_pass
+auth requisite pam_succeed_if.so uid >= 500 quiet
+auth required pam_deny.so
+
+{% if os_auth_retries > 0 %}
+account required pam_tally2.so
+{% endif %}
+account required pam_unix.so
+account sufficient pam_localuser.so
+account sufficient pam_succeed_if.so uid < 500 quiet
+account required pam_permit.so
+
+{% if (os_auth_pam_passwdqc_enable|bool) %}
+ {%- if ((ansible_os_family == 'RedHat' and ansible_distribution_version >= '7') or ansible_distribution == 'Amazon') %}
+password required pam_pwquality.so {{ os_auth_pam_pwquality_options }}
+ {%- else %}
+password requisite pam_passwdqc.so {{ os_auth_pam_passwdqc_options }}
+ {%- endif %}
+{% else %}
+password requisite pam_cracklib.so try_first_pass retry=3 type=
+{% endif %}
+
+
+# NSA 2.3.3.5 Upgrade Password Hashing Algorithm to SHA-512
+# NSA 2.3.3.6 Limit Password Reuse
+password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok remember=5
+password required pam_deny.so
+
+session optional pam_keyinit.so revoke
+session required pam_limits.so
+session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
+session required pam_unix.so
diff --git a/roles/os-harden/templates/etc/profile.d/profile.conf.j2 b/roles/os-harden/templates/etc/profile.d/profile.conf.j2
new file mode 100644
index 0000000..60f4353
--- /dev/null
+++ b/roles/os-harden/templates/etc/profile.d/profile.conf.j2
@@ -0,0 +1,4 @@
+# {{ ansible_managed | comment }}
+
+# Disable core dumps via soft limits for all users. Compliance to this setting is voluntary and can be modified by users up to a hard limit. This setting is a sane default.
+ulimit -S -c 0 > /dev/null 2>&1
diff --git a/roles/os-harden/templates/etc/rhel_libuser.conf.j2 b/roles/os-harden/templates/etc/rhel_libuser.conf.j2
new file mode 100644
index 0000000..89c41cc
--- /dev/null
+++ b/roles/os-harden/templates/etc/rhel_libuser.conf.j2
@@ -0,0 +1,90 @@
+# See libuser.conf(5) for more information.
+
+# {{ ansible_managed | comment }}
+
+# Do not modify the default module list if you care about unattended calls
+# to programs (i.e., scripts) working!
+
+[import]
+# Data from these files is used when libuser.conf does not define a value.
+# The mapping is documented in the man page.
+login_defs = /etc/login.defs
+default_useradd = /etc/default/useradd
+
+[defaults]
+# The default (/usr/lib*/libuser) is usually correct
+# moduledir = /your/custom/directory
+
+# The following variables are usually imported:
+# skeleton = /etc/skel
+# mailspooldir = /var/mail
+
+# NSA 2.3.3.5 Upgrade Password Hashing Algorithm to SHA-512
+crypt_style = sha512
+
+modules = files shadow
+create_modules = files shadow
+# modules = files shadow ldap
+# create_modules = ldap
+
+[userdefaults]
+LU_USERNAME = %n
+# LU_UIDNUMBER = 500
+LU_GIDNUMBER = %u
+# LU_USERPASSWORD = !!
+# LU_GECOS = %n
+# LU_HOMEDIRECTORY = /home/%n
+# LU_LOGINSHELL = /bin/bash
+
+# LU_SHADOWNAME = %n
+# LU_SHADOWPASSWORD = !!
+# LU_SHADOWLASTCHANGE = %d
+# LU_SHADOWMIN = 0
+# LU_SHADOWMAX = 99999
+# LU_SHADOWWARNING = 7
+# LU_SHADOWINACTIVE = -1
+# LU_SHADOWEXPIRE = -1
+# LU_SHADOWFLAG = -1
+
+[groupdefaults]
+LU_GROUPNAME = %n
+# LU_GIDNUMBER = 500
+# LU_GROUPPASSWORD = !!
+# LU_MEMBERUID =
+# LU_ADMINISTRATORUID =
+
+[files]
+# This is useful for the case where some master files are used to
+# populate a different NSS mechanism which this workstation uses.
+# directory = /etc
+
+[shadow]
+# This is useful for the case where some master files are used to
+# populate a different NSS mechanism which this workstation uses.
+# directory = /etc
+
+[ldap]
+# Setting these is always necessary.
+# server = ldap
+# basedn = dc=example,dc=com
+
+# Setting these is rarely necessary, since it's usually correct.
+# userBranch = ou=People
+# groupBranch = ou=Group
+
+# Set only if your administrative user uses simple bind operations to
+# connect to the server.
+# binddn = cn=Manager,dc=example,dc=com
+
+# Set this only if the default user (as determined by SASL) is incorrect
+# for SASL bind operations. Usually, it's correct, so you'll rarely need
+# to set these.
+# user = Manager
+# authuser = Manager
+
+[sasl]
+# Set these only if your sasldb is only used by a particular application, and
+# in a particular domain. The default (all applications, all domains) is
+# probably correct for most installations.
+# appname = imap
+# domain = EXAMPLE.COM
diff --git a/roles/os-harden/templates/etc/securetty.j2 b/roles/os-harden/templates/etc/securetty.j2
new file mode 100644
index 0000000..6c7394c
--- /dev/null
+++ b/roles/os-harden/templates/etc/securetty.j2
@@ -0,0 +1,6 @@
+# {{ ansible_managed | comment }}
+
+
+# A list of TTYs, from which root can log in
+# see `man securetty` for reference
+{{ "\n".join(os_auth_root_ttys) }}
diff --git a/roles/os-harden/templates/etc/sysconfig/rhel_sysconfig_init.j2 b/roles/os-harden/templates/etc/sysconfig/rhel_sysconfig_init.j2
new file mode 100644
index 0000000..1d2e466
--- /dev/null
+++ b/roles/os-harden/templates/etc/sysconfig/rhel_sysconfig_init.j2
@@ -0,0 +1,31 @@
+# {{ ansible_managed | comment }}
+
+# color => new RH6.0 bootup
+# verbose => old-style bootup
+# anything else => new style bootup without ANSI colors or positioning
+BOOTUP=color
+# column to start "[ OK ]" label in
+RES_COL=60
+# terminal sequence to move to that column. You could change this
+# to something like "tput hpa ${RES_COL}" if your terminal supports it
+MOVE_TO_COL="echo -en \\033[${RES_COL}G"
+# terminal sequence to set color to a 'success' color (currently: green)
+SETCOLOR_SUCCESS="echo -en \\033[0;32m"
+# terminal sequence to set color to a 'failure' color (currently: red)
+SETCOLOR_FAILURE="echo -en \\033[0;31m"
+# terminal sequence to set color to a 'warning' color (currently: yellow)
+SETCOLOR_WARNING="echo -en \\033[0;33m"
+# terminal sequence to reset to the default color.
+SETCOLOR_NORMAL="echo -en \\033[0;39m"
+# Set to anything other than 'no' to allow hotkey interactive startup...
+PROMPT={{ 'yes' if (os_security_init_prompt|bool) else 'no' }}
+# Set to 'yes' to allow probing for devices with swap signatures
+AUTOSWAP=no
+# What ttys should gettys be started on?
+ACTIVE_CONSOLES=/dev/tty[1-6]
+# Set to '/sbin/sulogin' to prompt for password on single-user mode
+# Set to '/sbin/sushell' otherwise
+SINGLE={{ '/sbin/sulogin' if os_security_init_single else '/sbin/sushell' }}
+
+# NSA 2.2.4.1 Set Daemon umask
+umask 027
diff --git a/roles/os-harden/templates/usr/share/pam-configs/pam_passwdqd.j2 b/roles/os-harden/templates/usr/share/pam-configs/pam_passwdqd.j2
new file mode 100644
index 0000000..bbfaedf
--- /dev/null
+++ b/roles/os-harden/templates/usr/share/pam-configs/pam_passwdqd.j2
@@ -0,0 +1,7 @@
+Name: passwdqc password strength enforcement
+Default: yes
+Priority: 1024
+Conflicts: cracklib
+Password-Type: Primary
+Password:
+ requisite pam_passwdqc.so {{ os_auth_pam_passwdqc_options }}
diff --git a/roles/os-harden/templates/usr/share/pam-configs/pam_tally2.j2 b/roles/os-harden/templates/usr/share/pam-configs/pam_tally2.j2
new file mode 100644
index 0000000..2b61950
--- /dev/null
+++ b/roles/os-harden/templates/usr/share/pam-configs/pam_tally2.j2
@@ -0,0 +1,10 @@
+Name: tally2 lockout after failed attempts enforcement
+Default: yes
+Priority: 1024
+Conflicts: cracklib
+Auth-Type: Primary
+Auth-Initial:
+ required pam_tally2.so deny={{ os_auth_retries }} onerr=fail unlock_time={{ os_auth_lockout_time }}
+Account-Type: Primary
+Account-Initial:
+ required pam_tally2.so
diff --git a/roles/os-harden/vars/Amazon.yml b/roles/os-harden/vars/Amazon.yml
new file mode 100644
index 0000000..fa53dd2
--- /dev/null
+++ b/roles/os-harden/vars/Amazon.yml
@@ -0,0 +1,9 @@
+---
+# system accounts that do not get their login disabled and pasword changed
+os_always_ignore_users: ['root','sync','shutdown','halt', 'ec2-user']
+
+sysctl_rhel_config:
+ # ExecShield protection against buffer overflows
+ kernel.exec-shield: 1
+ # Syncookies is used to prevent SYN-flooding attacks.
+ net.ipv4.tcp_syncookies: 1
diff --git a/roles/os-harden/vars/Debian.yml b/roles/os-harden/vars/Debian.yml
new file mode 100644
index 0000000..be28470
--- /dev/null
+++ b/roles/os-harden/vars/Debian.yml
@@ -0,0 +1,31 @@
+os_packages_pam_ccreds: 'libpam-ccreds'
+os_packages_pam_passwdqc: 'libpam-passwdqc'
+os_packages_pam_cracklib: 'libpam-cracklib'
+passwdqc_path: '/usr/share/pam-configs/passwdqc'
+tally2_path: '/usr/share/pam-configs/tally2'
+os_nologin_shell_path: '/usr/sbin/nologin'
+
+auditd_package: 'auditd'
+modprobe_package: 'kmod'
+
+# Different distros use different standards for /etc/shadow perms, e.g.
+# RHEL derivatives use root:root 0000, whereas Debian-based use root:shadow 0640.
+# You must provide key/value pairs for owner, group, and mode if overriding.
+os_shadow_perms:
+ owner: root
+ group: shadow
+ mode: '0640'
+
+os_passwd_perms:
+ owner: root
+ group: root
+ mode: '0644'
+
+os_env_umask: '027'
+
+os_auth_uid_min: 1000
+os_auth_gid_min: 1000
+os_auth_sys_uid_min: 100
+os_auth_sys_uid_max: 999
+os_auth_sys_gid_min: 100
+os_auth_sys_gid_max: 999
diff --git a/roles/os-harden/vars/Oracle Linux.yml b/roles/os-harden/vars/Oracle Linux.yml
new file mode 100644
index 0000000..887473a
--- /dev/null
+++ b/roles/os-harden/vars/Oracle Linux.yml
@@ -0,0 +1,26 @@
+os_packages_pam_ccreds: 'pam_ccreds'
+os_packages_pam_passwdqc: 'pam_passwdqc'
+os_packages_pam_cracklib: 'pam_cracklib'
+os_nologin_shell_path: '/sbin/nologin'
+
+# Different distros use different standards for /etc/shadow perms, e.g.
+# RHEL derivatives use root:root 0000, whereas Debian-based use root:shadow 0640.
+# You must provide key/value pairs for owner, group, and mode if overriding.
+os_shadow_perms:
+ owner: root
+ group: root
+ mode: '0000'
+
+os_passwd_perms:
+ owner: root
+ group: root
+ mode: '0644'
+
+os_env_umask: '077'
+
+os_auth_uid_min: 1000
+os_auth_gid_min: 1000
+os_auth_sys_uid_min: 201
+os_auth_sys_uid_max: 999
+os_auth_sys_gid_min: 201
+os_auth_sys_gid_max: 999
diff --git a/roles/os-harden/vars/RedHat-6.yml b/roles/os-harden/vars/RedHat-6.yml
new file mode 100644
index 0000000..eec7efd
--- /dev/null
+++ b/roles/os-harden/vars/RedHat-6.yml
@@ -0,0 +1,7 @@
+---
+
+sysctl_rhel_config:
+ # ExecShield protection against buffer overflows
+ kernel.exec-shield: 1
+ # Syncookies is used to prevent SYN-flooding attacks.
+ net.ipv4.tcp_syncookies: 1
diff --git a/roles/os-harden/vars/RedHat.yml b/roles/os-harden/vars/RedHat.yml
new file mode 100644
index 0000000..9624cc2
--- /dev/null
+++ b/roles/os-harden/vars/RedHat.yml
@@ -0,0 +1,31 @@
+---
+
+modprobe_package: 'module-init-tools'
+auditd_package: 'audit'
+
+os_packages_pam_ccreds: 'pam_ccreds'
+os_packages_pam_passwdqc: 'pam_passwdqc'
+os_packages_pam_cracklib: 'pam_cracklib'
+os_nologin_shell_path: '/sbin/nologin'
+
+# Different distros use different standards for /etc/shadow perms, e.g.
+# RHEL derivatives use root:root 0000, whereas Debian-based use root:shadow 0640.
+# You must provide key/value pairs for owner, group, and mode if overriding.
+os_shadow_perms:
+ owner: root
+ group: root
+ mode: '0000'
+
+os_passwd_perms:
+ owner: root
+ group: root
+ mode: '0644'
+
+os_env_umask: '077'
+
+os_auth_uid_min: 1000
+os_auth_gid_min: 1000
+os_auth_sys_uid_min: 201
+os_auth_sys_uid_max: 999
+os_auth_sys_gid_min: 201
+os_auth_sys_gid_max: 999
diff --git a/roles/os-harden/vars/main.yml b/roles/os-harden/vars/main.yml
new file mode 100644
index 0000000..886ceef
--- /dev/null
+++ b/roles/os-harden/vars/main.yml
@@ -0,0 +1,110 @@
+# SYSTEM CONFIGURATION
+# ====================
+# These are not meant to be modified by the user
+
+# suid and sgid blacklists and whitelists
+# ---------------------------------------
+# don't change values in the system_blacklist/whitelist
+# adjust values for blacklist/whitelist instead, they can override system_blacklist/whitelist
+
+# list of suid/sgid entries that must be removed
+os_security_suid_sgid_system_blacklist:
+ # blacklist as provided by NSA
+ - '/usr/bin/rcp'
+ - '/usr/bin/rlogin'
+ - '/usr/bin/rsh'
+ # sshd must not use host-based authentication (see ssh cookbook)
+ - '/usr/libexec/openssh/ssh-keysign'
+ - '/usr/lib/openssh/ssh-keysign'
+ # misc others
+ - '/sbin/netreport' # not normally required for user
+ - '/usr/sbin/usernetctl' # modify interfaces via functional accounts
+ # connecting to ...
+ - '/usr/sbin/userisdnctl' # no isdn...
+ - '/usr/sbin/pppd' # no ppp / dsl ...
+ # lockfile
+ - '/usr/bin/lockfile'
+ - '/usr/bin/mail-lock'
+ - '/usr/bin/mail-unlock'
+ - '/usr/bin/mail-touchlock'
+ - '/usr/bin/dotlockfile'
+ # need more investigation blacklist for now
+ - '/usr/bin/arping'
+ - '/usr/sbin/uuidd'
+ - '/usr/bin/mtr' # investigate current state...
+ - '/usr/lib/evolution/camel-lock-helper-1.2' # investigate current state...
+ - '/usr/lib/pt_chown' # pseudo-tty needed?
+ - '/usr/lib/eject/dmcrypt-get-device'
+ - '/usr/lib/mc/cons.saver' # midnight commander screensaver
+
+# list of suid/sgid entries that can remain untouched
+os_security_suid_sgid_system_whitelist:
+ # whitelist as provided by NSA
+ - '/bin/mount'
+ - '/bin/ping'
+ - '/bin/su'
+ - '/bin/umount'
+ - '/sbin/pam_timestamp_check'
+ - '/sbin/unix_chkpwd'
+ - '/usr/bin/at'
+ - '/usr/bin/gpasswd'
+ - '/usr/bin/locate'
+ - '/usr/bin/newgrp'
+ - '/usr/bin/passwd'
+ - '/usr/bin/ssh-agent'
+ - '/usr/libexec/utempter/utempter'
+ - '/usr/sbin/lockdev'
+ - '/usr/sbin/sendmail.sendmail'
+ - '/usr/bin/expiry'
+ # whitelist ipv6
+ - '/bin/ping6'
+ - '/usr/bin/traceroute6.iputils'
+ # whitelist nfs
+ - '/sbin/mount.nfs'
+ - '/sbin/umount.nfs'
+ # whitelist nfs4
+ - '/sbin/mount.nfs4'
+ - '/sbin/umount.nfs4'
+ # whitelist cron
+ - '/usr/bin/crontab'
+ # whitelist consolemssaging
+ - '/usr/bin/wall'
+ - '/usr/bin/write'
+ # whitelist: only SGID with utmp group for multi-session access
+ # impact is limited; installation/usage has some remaining risk
+ - '/usr/bin/screen'
+ # whitelist locate
+ - '/usr/bin/mlocate'
+ # whitelist usermanagement
+ - '/usr/bin/chage'
+ - '/usr/bin/chfn'
+ - '/usr/bin/chsh'
+ # whitelist fuse
+ - '/bin/fusermount'
+ # whitelist pkexec
+ - '/usr/bin/pkexec'
+ # whitelist sudo
+ - '/usr/bin/sudo'
+ - '/usr/bin/sudoedit'
+ # whitelist postfix
+ - '/usr/sbin/postdrop'
+ - '/usr/sbin/postqueue'
+ # whitelist apache
+ - '/usr/sbin/suexec'
+ # whitelist squid
+ - '/usr/lib/squid/ncsa_auth'
+ - '/usr/lib/squid/pam_auth'
+ # whitelist kerberos
+ - '/usr/kerberos/bin/ksu'
+ # whitelist pam_caching
+ - '/usr/sbin/ccreds_validate'
+ # whitelist Xorg
+ - '/usr/bin/Xorg' # xorg
+ - '/usr/bin/X' # xorg
+ - '/usr/lib/dbus-1.0/dbus-daemon-launch-helper' # freedesktop ipc
+ - '/usr/lib/vte/gnome-pty-helper' # gnome
+ - '/usr/lib/libvte9/gnome-pty-helper' # gnome
+ - '/usr/lib/libvte-2.90-9/gnome-pty-helper' # gnome
+
+# system accounts that do not get their login disabled and pasword changed
+os_always_ignore_users: ['root','sync','shutdown','halt']
diff --git a/roles/prepare/tasks/centos.yml b/roles/prepare/tasks/centos.yml
new file mode 100644
index 0000000..96b170f
--- /dev/null
+++ b/roles/prepare/tasks/centos.yml
@@ -0,0 +1,34 @@
+- name: 删除centos/redhat默认安装
+ yum:
+ name:
+ - firewalld
+ - python-firewall
+ - firewalld-filesystem
+ state: absent
+
+- name: 添加EPEL仓库
+ yum: name=epel-release state=latest
+
+- name: 安装基础软件包
+ yum:
+ name:
+ - conntrack-tools # ipvs 模式需要
+ - psmisc # 安装psmisc 才能使用命令killall,它在keepalive的监测脚本中使用到
+ - nfs-utils # 挂载nfs 共享文件需要 (创建基于 nfs的PV 需要)
+ - jq # 轻量JSON处理程序,安装docker查询镜像需要
+ - socat # 用于port forwarding
+ - bash-completion # bash命令补全工具,需要重新登录服务器生效
+ - rsync # 文件同步工具,分发证书等配置文件需要
+ - ipset
+ - ipvsadm
+ state: latest
+
+- name: 临时关闭 selinux
+ shell: "setenforce 0"
+ failed_when: false
+
+- name: 永久关闭 selinux
+ lineinfile:
+ dest: /etc/selinux/config
+ regexp: "^SELINUX="
+ line: "SELINUX=disabled"
diff --git a/roles/prepare/tasks/common.yml b/roles/prepare/tasks/common.yml
new file mode 100644
index 0000000..51c40fa
--- /dev/null
+++ b/roles/prepare/tasks/common.yml
@@ -0,0 +1,41 @@
+# 禁用系统swap
+- name: 禁用系统 swap
+ shell: "swapoff -a && sysctl -w vm.swappiness=0"
+ ignore_errors: true
+
+- name: 删除fstab swap 相关配置
+ lineinfile:
+ path: /etc/fstab
+ regexp: 'swap'
+ state: absent
+ backup: 'yes'
+
+- name: 加载内核模块
+ modprobe: name={{ item }} state=present
+ with_items:
+ - br_netfilter
+ - ip_vs
+ - ip_vs_rr
+ - ip_vs_wrr
+ - ip_vs_sh
+ - nf_conntrack_ipv4 # kernel < 4.19
+ - nf_conntrack # kernel >= 4.19
+ ignore_errors: true
+
+- name: 启用systemd自动加载模块服务
+ service: name=systemd-modules-load enabled=yes
+
+- name: 增加内核模块开机加载配置
+ template: src=10-k8s-modules.conf.j2 dest=/etc/modules-load.d/10-k8s-modules.conf
+
+# 设置系统参数for k8s
+# 消除docker info 警告WARNING: bridge-nf-call-ip[6]tables is disabled
+- name: 设置系统参数
+ template: src=95-k8s-sysctl.conf.j2 dest=/etc/sysctl.d/95-k8s-sysctl.conf
+
+- name: 生效系统参数
+ shell: "sysctl -p /etc/sysctl.d/95-k8s-sysctl.conf"
+ ignore_errors: true
+
+- name: 设置系统 ulimits
+ template: src=30-k8s-ulimits.conf.j2 dest=/etc/security/limits.d/30-k8s-ulimits.conf
diff --git a/roles/prepare/tasks/debian.yml b/roles/prepare/tasks/debian.yml
new file mode 100644
index 0000000..0e55bc1
--- /dev/null
+++ b/roles/prepare/tasks/debian.yml
@@ -0,0 +1,30 @@
+# 删除默认安装
+- name: 删除ubuntu默认安装
+ apt:
+ name:
+ - ufw
+ - lxd
+ - lxd-client
+ - lxcfs
+ - lxc-common
+ state: absent
+
+# Ubuntu 安装基础软件包
+- name: 安装 ubuntu基础软件
+ apt:
+ name:
+ - nfs-common # 挂载nfs 共享文件需要 (创建基于 nfs的PV 需要)
+ - conntrack # network connection cleanup 用到
+ - jq # 轻量JSON处理程序,安装docker查询镜像需要
+ - socat # 用于port forwarding
+ - bash-completion # bash命令补全工具,需要重新登录服务器生效
+ - rsync # 文件同步工具,分发证书等配置文件需要
+ - ipset
+ - ipvsadm
+ state: latest
+
+- name: 设置 ulimits
+ lineinfile:
+ dest: /etc/profile
+ regexp: "ulimit -SHn"
+ line: "ulimit -SHn 65535"
diff --git a/roles/prepare/tasks/main.yml b/roles/prepare/tasks/main.yml
new file mode 100644
index 0000000..eb6b30d
--- /dev/null
+++ b/roles/prepare/tasks/main.yml
@@ -0,0 +1,39 @@
+# 系统基础软件环境
+- import_tasks: debian.yml
+ when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"
+
+- import_tasks: centos.yml
+ when: ansible_distribution == "CentOS" or ansible_distribution == "RedHat"
+
+# 公共系统参数设置
+- import_tasks: common.yml
+
+- name: prepare some dirs
+ file: name={{ item }} state=directory
+ with_items:
+ - "{{ bin_dir }}"
+ - "{{ ca_dir }}"
+
+- name: 分发证书工具 CFSSL
+ copy: src={{ base_dir }}/bin/{{ item }} dest={{ bin_dir }}/{{ item }} mode=0755
+ with_items:
+ - cfssl
+ - cfssl-certinfo
+ - cfssljson
+
+- name: 本地设置 bin 目录权限
+ file: path={{ base_dir }}/bin state=directory mode=0755 recurse=yes
+ connection: local
+ run_once: true
+
+- name: 本地创建 easzctl 工具的软连接
+ file: src={{ base_dir }}/tools/easzctl dest=/usr/bin/easzctl state=link
+ connection: local
+ run_once: true
+
+- name: 写入环境变量$PATH
+ lineinfile:
+ dest: ~/.bashrc
+ state: present
+ regexp: 'kubeasz'
+ line: 'export PATH={{ bin_dir }}:$PATH # generated by kubeasz'
diff --git a/roles/prepare/templates/10-k8s-modules.conf.j2 b/roles/prepare/templates/10-k8s-modules.conf.j2
new file mode 100644
index 0000000..5c48d30
--- /dev/null
+++ b/roles/prepare/templates/10-k8s-modules.conf.j2
@@ -0,0 +1,7 @@
+br_netfilter
+ip_vs
+ip_vs_rr
+ip_vs_wrr
+ip_vs_sh
+nf_conntrack_ipv4
+nf_conntrack
diff --git a/roles/prepare/templates/30-k8s-ulimits.conf.j2 b/roles/prepare/templates/30-k8s-ulimits.conf.j2
new file mode 100644
index 0000000..e9abdd2
--- /dev/null
+++ b/roles/prepare/templates/30-k8s-ulimits.conf.j2
@@ -0,0 +1,4 @@
+* soft nofile 65536
+* hard nofile 65536
+* soft nproc 65536
+* hard nproc 65536
diff --git a/roles/prepare/templates/95-k8s-sysctl.conf.j2 b/roles/prepare/templates/95-k8s-sysctl.conf.j2
new file mode 100644
index 0000000..b1255a8
--- /dev/null
+++ b/roles/prepare/templates/95-k8s-sysctl.conf.j2
@@ -0,0 +1,9 @@
+net.ipv4.ip_forward = 1
+net.bridge.bridge-nf-call-iptables = 1
+net.bridge.bridge-nf-call-ip6tables = 1
+net.bridge.bridge-nf-call-arptables = 1
+#
+net.netfilter.nf_conntrack_max=1000000
+vm.swappiness = 0
+vm.max_map_count=655360
+fs.file-max=655360
diff --git a/test.md b/test.md
index e69de29..2d9c28c 100644
--- a/test.md
+++ b/test.md
@@ -0,0 +1 @@
+rererererr
diff --git a/tools/19.addetcd.yml b/tools/19.addetcd.yml
new file mode 100644
index 0000000..8ef803d
--- /dev/null
+++ b/tools/19.addetcd.yml
@@ -0,0 +1,25 @@
+# Note: this playbook cann't run independently
+# Usage: easzctl add-etcd 1.1.1.1
+
+# add new-etcd node, one at a time
+- hosts: "{{ NODE_TO_ADD }}"
+ tasks:
+ - name: add a new etcd member
+ shell: "ETCDCTL_API=3 {{ bin_dir }}/etcdctl member add {{ NODE_NAME }} --peer-urls=https://{{ NODE_TO_ADD }}:2380"
+ delegate_to: "{{ groups.etcd[1] }}"
+
+# start the new-etcd node
+- hosts: "{{ NODE_TO_ADD }}"
+ vars:
+ CLUSTER_STATE: existing
+ roles:
+ - { role: chrony, when: "hostvars[groups.deploy[0]]['NTP_ENABLED'] == 'yes'" }
+ - prepare
+ - etcd
+
+# restart the original etcd cluster with the new configuration
+- hosts: etcd
+ vars:
+ CLUSTER_STATE: existing
+ roles:
+ - etcd
diff --git a/tools/20.addnode.yml b/tools/20.addnode.yml
new file mode 100644
index 0000000..3ca6e75
--- /dev/null
+++ b/tools/20.addnode.yml
@@ -0,0 +1,13 @@
+# Note: this playbook cann't run independently
+# Usage: easzctl add-node 1.1.1.1
+
+- hosts: "{{ NODE_TO_ADD }}"
+ roles:
+ - { role: chrony, when: "hostvars[groups.deploy[0]]['NTP_ENABLED'] == 'yes'" }
+ - prepare
+ - docker
+ - kube-node
+ - { role: calico, when: "CLUSTER_NETWORK == 'calico'" }
+ - { role: cilium, when: "CLUSTER_NETWORK == 'cilium'" }
+ - { role: flannel, when: "CLUSTER_NETWORK == 'flannel'" }
+ - { role: kube-router, when: "CLUSTER_NETWORK == 'kube-router'" }
diff --git a/tools/21.addmaster.yml b/tools/21.addmaster.yml
new file mode 100644
index 0000000..c83ef85
--- /dev/null
+++ b/tools/21.addmaster.yml
@@ -0,0 +1,31 @@
+# Note: this playbook cann't run independently
+# Usage: easzctl add-master 1.1.1.1
+
+- hosts: "{{ NODE_TO_ADD }}"
+ roles:
+ - { role: chrony, when: "hostvars[groups.deploy[0]]['NTP_ENABLED'] == 'yes'" }
+ - prepare
+ - docker
+ - kube-master
+ - kube-node
+ - { role: calico, when: "CLUSTER_NETWORK == 'calico'" }
+ - { role: cilium, when: "CLUSTER_NETWORK == 'cilium'" }
+ - { role: flannel, when: "CLUSTER_NETWORK == 'flannel'" }
+ - { role: kube-router, when: "CLUSTER_NETWORK == 'kube-router'" }
+ #
+ tasks:
+ - name: Making master nodes SchedulingDisabled
+ shell: "{{ bin_dir }}/kubectl cordon {{ NODE_TO_ADD }} "
+ delegate_to: "{{ groups.deploy[0] }}"
+ when: DEPLOY_MODE != "allinone"
+ ignore_errors: true
+
+ - name: Setting master role name
+ shell: "{{ bin_dir }}/kubectl label node {{ NODE_TO_ADD }} kubernetes.io/role=master --overwrite"
+ ignore_errors: true
+ delegate_to: "{{ groups.deploy[0] }}"
+
+# reconfigure and restart the haproxy service
+- hosts: lb
+ roles:
+ - { role: lb, tags:['restart_lb'] }
diff --git a/tools/basic-env-setup.sh b/tools/basic-env-setup.sh
new file mode 100644
index 0000000..c55f9f6
--- /dev/null
+++ b/tools/basic-env-setup.sh
@@ -0,0 +1,152 @@
+#!/bin/bash
+
+# 本脚本提供如下功能,作者:Joey Yang, https://github.com/Code2Life
+# 1. 在Ubuntu/CentOS/Fedora/ArchLinux中自动化的安装python+ansible;
+# 2. clone kubeasz项目代码, 并将需要的二进制文件下载解压到/etc/ansible/bin中;
+# 另外, 相关的k8s二进制文件, 我同步到了个人在七牛上的CDN存储中(速度更快), 方便大家下载: filecdn.code2life.top;
+#
+# 使用方法:
+# 1. 支持带参数的运行, 如: ./basic-env-setup.sh k8s.193.tar.gz 指定不同的kubernetes二进制文件, 无参数时默认最新的k8s.1100.tar.gz (k8s 1.10.0 + etcd 3.3.2).
+# 2. 也可以在任何一台支持的linux设备运行:curl http://filecdn.code2life.top/kubeasz-basic-env-setup.sh | sh -s
+# 已经亲测centos7/ubuntu16.04/debian9/fedora27都是可以的, 二进制包下载速度贼快.脚本运行完毕之后, 只需到/etc/ansible目录下配置好hosts, 复制完ssh的公钥即可通过ansible-playbook迅速搭建集群了.
+
+set -e
+
+# curl http://filecdn.code2life.top/kubeasz-basic-env-setup.sh | sh -s
+
+# 默认1.10.0 版本的 Kubernetes
+bin_resource_url='http://filecdn.code2life.top/k8s.1100.tar.gz'
+
+# 如果参数指定k8s相关的bin以指定的为准, 例如: k8s.193.tar.gz
+if [ "$1" ];then
+ bin_resource_url="http://filecdn.code2life.top/"$1
+fi
+
+# 各Linux版本安装python/pip
+# ---------------------------
+
+# debian 默认的apt源在国内访问很慢, 可手动修改/etc/apt/source.list修改为其他源
+# 以 debian 9 为例, source.list可修改为如下内容, ubuntu修改方法类似, 找到相应系统和版本的镜像源替换即可
+# deb http://mirrors.163.com/debian/ stretch main non-free contrib
+# deb http://mirrors.163.com/debian/ stretch-updates main non-free contrib
+# deb http://mirrors.163.com/debian/ stretch-backports main non-free contrib
+# deb http://mirrors.163.com/debian-security/ stretch/updates main non-free contrib
+basic_ubuntu_debian() {
+ echo "Setup Basic Environment for Ubuntu/Debian."
+ apt-get update && apt-get upgrade -y && apt-get dist-upgrade -y
+ apt-get install python2.7 git python-pip curl -y
+
+ if [ ! -f /usr/bin/python ];then
+ ln -s /usr/bin/python2.7 /usr/bin/python
+ fi
+}
+
+# 红帽系Liunx可修改yum源加快下载速度, 修改/etc/yum.repos.d内文件即可
+basic_centos() {
+ echo "Setup Basic Environment for CentOS."
+ yum install epel-release -y
+ yum update -y
+ yum erase firewalld firewalld-filesystem python-firewall -y
+ yum install git python python-pip curl -y
+}
+
+basic_fedora() {
+ echo "Setup Basic Environment for Fedora."
+ yum update -y
+ yum install git python python-pip curl -y
+}
+
+# archlinux 使用pacman进行包管理
+basic_arch() {
+ pacman -Syu --noconfirm
+ pacman -S python git python-pip curl --noconfirm
+}
+
+# 使用pip安装ansible, 并下载k8s相关bin文件
+setup_ansible_k8s() {
+ echo "Download Ansible and Kubernetes binaries."
+ pip install pip --upgrade -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
+ pip install --no-cache-dir ansible -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
+
+ git clone https://github.com/gjmzj/kubeasz.git
+ mv kubeasz /etc/ansible
+
+ # Download from CDN & Move bin files
+ curl -o k8s_download.tar.gz "$bin_resource_url"
+ tar zxvf k8s_download.tar.gz
+ mv -f bin/* /etc/ansible/bin
+ rm -rf bin
+ echo "Finish setup. Please config your hosts and run 'ansible-playbook' command at /etc/ansible."
+}
+# ---------------------------
+
+# 判断Linux发行版, 执行不同基础环境设置方法
+# ---------------------------
+lsb_dist=''
+command_exists() {
+ command -v "$@" > /dev/null 2>&1
+}
+if command_exists lsb_release; then
+ lsb_dist="$(lsb_release -si)"
+ lsb_version="$(lsb_release -rs)"
+fi
+if [ -z "$lsb_dist" ] && [ -r /etc/lsb-release ]; then
+ lsb_dist="$(. /etc/lsb-release && echo "$DISTRIB_ID")"
+ lsb_version="$(. /etc/lsb-release && echo "$DISTRIB_RELEASE")"
+fi
+if [ -z "$lsb_dist" ] && [ -r /etc/debian_version ]; then
+ lsb_dist='debian'
+fi
+if [ -z "$lsb_dist" ] && [ -r /etc/fedora-release ]; then
+ lsb_dist='fedora'
+fi
+if [ -z "$lsb_dist" ] && [ -r /etc/os-release ]; then
+ lsb_dist="$(. /etc/os-release && echo "$ID")"
+fi
+if [ -z "$lsb_dist" ] && [ -r /etc/centos-release ]; then
+ lsb_dist="$(cat /etc/*-release | head -n1 | cut -d " " -f1)"
+fi
+if [ -z "$lsb_dist" ] && [ -r /etc/redhat-release ]; then
+ lsb_dist="$(cat /etc/*-release | head -n1 | cut -d " " -f1)"
+fi
+lsb_dist="$(echo $lsb_dist | cut -d " " -f1)"
+lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')"
+# ---------------------------
+
+# ---------------------------
+setup_env(){
+ case "$lsb_dist" in
+ centos)
+ basic_centos
+ setup_ansible_k8s
+ exit 0
+ ;;
+ fedora)
+ basic_fedora
+ setup_ansible_k8s
+ exit 0
+ ;;
+ ubuntu)
+ basic_ubuntu_debian
+ setup_ansible_k8s
+ exit 0
+ ;;
+ debian)
+ basic_ubuntu_debian
+ setup_ansible_k8s
+ exit 0
+ ;;
+ arch)
+ basic_arch
+ setup_ansible_k8s
+ exit 0
+ ;;
+ suse)
+ echo 'Not implementation yet.'
+ exit 1
+ esac
+ echo "Error: Unsupported OS, please set ansible environment manually."
+ exit 1
+}
+setup_env
+# ---------------------------
diff --git a/tools/change_ip_aio.yml b/tools/change_ip_aio.yml
new file mode 100644
index 0000000..11a0be3
--- /dev/null
+++ b/tools/change_ip_aio.yml
@@ -0,0 +1,42 @@
+# 敬告:本脚本仅作测试交流使用,详细操作说明请参阅docs/op/change_ip_allinone.md
+# 此脚本仅用于allinone部署情况下,需要修改host ip地址使用(比如,虚机装完allinone部署,克隆或者复制分享给别人测试使用)
+#
+# ------- 前提 :一个运行正常的allinone部署在虚机,关机后复制给别人使用,新虚机开机后如果需要修改IP,请执行如下步骤
+# 0. 拉取最新项目代码:git pull origin master
+# 1. 修改ansible hosts文件:sed -i 's/$OLD_IP/$NEW_IP/g' /etc/ansible/hosts
+# 2. 配置ssh免密码登陆:ssh-copy-id $NEW_IP 按提示完成
+# 3. 检查下修改是否成功,并且能够成功执行 ansible all -m ping
+
+- hosts: deploy # hosts 角色无所谓,反正allinone所有角色都是同个ip
+ tasks:
+ - name: 删除一些证书和配置,后面会以新IP重新生成
+ file: name={{ item }} state=absent
+ with_items:
+ - "/etc/etcd/ssl/etcd.pem" # 删除etcd证书
+ - "/etc/kubernetes/ssl/kubernetes.pem" # 删除旧master证书
+ - "/etc/kubernetes/kubelet.kubeconfig" # 删除旧kubelet配置文件
+
+- hosts: deploy
+ roles:
+ - deploy
+ - etcd
+ - kube-master
+ - kube-node
+
+- hosts: deploy
+ tasks:
+ - name: 删除老IP地址的node
+ shell: "{{ bin_dir }}/kubectl get node |grep -v '{{ inventory_hostname }}'|awk '{print $1}' |xargs {{ bin_dir }}/kubectl delete node"
+ ignore_errors: true
+
+ - name: 删除原network插件部署
+ shell: "{{ bin_dir }}/kubectl delete -f /opt/kube/kube-system/{{ CLUSTER_NETWORK }}/ || \
+ {{ bin_dir }}/kubectl delete -f /root/local/kube-system/{{ CLUSTER_NETWORK }}/"
+ ignore_errors: true
+
+- hosts: deploy
+ roles:
+ - { role: calico, when: "CLUSTER_NETWORK == 'calico'" }
+ - { role: cilium, when: "CLUSTER_NETWORK == 'cilium'" }
+ - { role: flannel, when: "CLUSTER_NETWORK == 'flannel'" }
+ - { role: kube-router, when: "CLUSTER_NETWORK == 'kube-router'" }
diff --git a/tools/change_k8s_network.yml b/tools/change_k8s_network.yml
new file mode 100644
index 0000000..99b973f
--- /dev/null
+++ b/tools/change_k8s_network.yml
@@ -0,0 +1,115 @@
+# 重置k8s pod网络脚本,使用请仔细阅读 docs/op/change_k8s_network.md
+- hosts:
+ - kube-master
+ - kube-node
+ tasks:
+ - name: 获取所有已经创建的POD信息
+ command: "{{ bin_dir }}/kubectl get daemonset -n kube-system"
+ register: pod_info
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+ - name: 删除原network插件部署
+ shell: "{{ bin_dir }}/kubectl delete -f /opt/kube/kube-system/{{ item }}/"
+ with_items:
+ - calico
+ - cilium
+ - flannel
+ - kube-router
+ when: 'item in pod_info.stdout'
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+ ignore_errors: true
+
+ - name: 清理kube-router相关
+ shell: "{{ bin_dir }}/docker run --privileged --net=host cloudnativelabs/kube-router --cleanup-config"
+ ignore_errors: true
+ when: '"kube-router" in pod_info.stdout'
+
+ - name: 停止 kube-node 相关服务
+ service: name={{ item }} state=stopped
+ with_items:
+ - kubelet
+ - kube-proxy
+ ignore_errors: true
+
+ - name: 清理calico残留路由
+ shell: "for rt in `ip route|grep bird|sed 's/blackhole//'|awk '{print $1}'`;do ip route del $rt;done;"
+ when: '"calico" in pod_info.stdout'
+ ignore_errors: true
+
+ - name: 清理 kube-proxy产生的iptables规则
+ shell: "{{ bin_dir }}/kube-proxy --cleanup"
+ ignore_errors: true
+
+ - name: 清理目录和文件
+ file: name={{ item }} state=absent
+ with_items:
+ - "/etc/cni/"
+ - "/run/flannel/"
+ - "/etc/calico/"
+ - "/var/run/calico/"
+ - "/var/lib/calico/"
+ - "/var/log/calico/"
+ - "/etc/cilium/"
+ - "/var/run/cilium/"
+ - "/sys/fs/bpf/tc/"
+ - "/var/lib/cni/"
+ - "/var/lib/kube-router/"
+ - "/opt/kube/kube-system/"
+
+ - name: 清理网络
+ shell: "ip link del tunl0; \
+ ip link del flannel.1; \
+ ip link del cni0; \
+ ip link del mynet0; \
+ ip link del kube-bridge; \
+ ip link del dummy0; \
+ ip link del kube-ipvs0; \
+ ip link del cilium_net; \
+ ip link del cilium_vxlan; \
+ systemctl restart networking; \
+ systemctl restart network"
+ ignore_errors: true
+
+ - name: 开启 kube-node 相关服务
+ service: name={{ item }} state=started enabled=yes
+ with_items:
+ - kubelet
+ - kube-proxy
+ ignore_errors: true
+
+- hosts:
+ - lb
+ tasks:
+ - name: 重启lb的keepalived服务
+ service: name=keepalived state=restarted
+
+ - name: 轮询等待apiserver服务恢复
+ command: "{{ bin_dir }}/kubectl get node"
+ register: result
+ until: result.rc == 0
+ retries: 5
+ delay: 6
+ delegate_to: "{{ groups.deploy[0] }}"
+ run_once: true
+
+- hosts:
+ - kube-master
+ - kube-node
+ # 安装新的网络插件
+ roles:
+ - { role: calico, when: "CLUSTER_NETWORK == 'calico'" }
+ - { role: cilium, when: "CLUSTER_NETWORK == 'cilium'" }
+ - { role: flannel, when: "CLUSTER_NETWORK == 'flannel'" }
+ - { role: kube-router, when: "CLUSTER_NETWORK == 'kube-router'" }
+
+- hosts: deploy
+ tasks:
+ # 删除所有运行pod,由controller自动重建
+ - name: 重启所有pod
+ shell: "for NS in $({{ bin_dir }}/kubectl get ns|awk 'NR>1{print $1}'); \
+ do {{ bin_dir }}/kubectl delete pod --all -n $NS; done;"
+ ignore_errors: true
+ run_once: true
+
diff --git a/tools/clean_one_node.yml b/tools/clean_one_node.yml
new file mode 100644
index 0000000..86be892
--- /dev/null
+++ b/tools/clean_one_node.yml
@@ -0,0 +1,227 @@
+# 警告:此脚本将清理单个node节点,使用请详细参阅 docs/op/clean_one_node.md
+# 请三思后运行此脚本,特别的:如果有pod使用了本地存储类型,请自行判断重要性
+# 使用:
+# 1. 执行 ansible-playbook /etc/ansible/tools/clean_one_node.yml
+# 2. 按照提示输入待删除节点
+
+- hosts: deploy
+ vars_prompt:
+ - name: "NODE_TO_DEL"
+ prompt: "which node is about to be deleted?(e.g 192.168.1.1)"
+ private: no
+ confirm: yes
+ tasks:
+ - name: fail info1
+ fail: msg="you CAN NOT delete the last member of etcd cluster!"
+ when: "groups['etcd']|length < 2 and NODE_TO_DEL in groups['etcd']"
+
+ - name: fail info2
+ fail: msg="you CAN NOT delete the last member of kube-master!"
+ when: "groups['kube-master']|length < 2 and NODE_TO_DEL in groups['kube-master']"
+
+ - name: 执行kubectl drain(节点可能是kube-node节点)
+ shell: "{{ bin_dir }}/kubectl drain {{ NODE_TO_DEL }} --ignore-daemonsets --delete-local-data"
+ ignore_errors: true
+
+ - block:
+ # 清理 kube-node 相关服务
+ - name: stop and disable kube-node service
+ service: name={{ item }} state=stopped enabled=no
+ with_items:
+ - kubelet
+ - kube-proxy
+ ignore_errors: true
+
+ - name: umount kubelet 挂载的目录
+ shell: "mount | grep '/var/lib/kubelet'| awk '{print $3}'|xargs umount"
+ args:
+ warn: false
+ ignore_errors: true
+
+ - name: 清理目录和文件
+ file: name={{ item }} state=absent
+ with_items:
+ - "/var/lib/kubelet/"
+ - "/var/lib/kube-proxy/"
+ - "/etc/systemd/system/kubelet.service"
+ - "/etc/systemd/system/kube-proxy.service"
+ - "/opt/kube/kube-system/"
+
+ # 清理 kube-master 相关
+ - name: stop and disable kube-master service
+ service: name={{ item }} state=stopped enabled=no
+ with_items:
+ - kube-apiserver
+ - kube-controller-manager
+ - kube-scheduler
+ ignore_errors: true
+
+ - name: 清理目录和文件
+ file: name={{ item }} state=absent
+ with_items:
+ - "/var/run/kubernetes"
+ - "/etc/systemd/system/kube-apiserver.service"
+ - "/etc/systemd/system/kube-controller-manager.service"
+ - "/etc/systemd/system/kube-scheduler.service"
+
+ # 清理集群docker服务、网络相关
+ - name: 清理kube-router相关
+ shell: "{{ bin_dir }}/docker run --privileged --net=host cloudnativelabs/kube-router --cleanup-config"
+ ignore_errors: true
+ when: "CLUSTER_NETWORK == 'kube-router'"
+
+ - name: 获取是否运行名为'kubeasz'的容器
+ shell: 'docker ps|grep kubeasz || echo "NOT FOUND"'
+ register: install_info
+
+ - name: stop and disable docker service
+ service:
+ name: docker
+ state: stopped
+ enabled: no
+ ignore_errors: true
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: unmount docker filesystem-1
+ mount: path=/var/run/docker/netns/default state=unmounted
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: unmount docker filesystem-2
+ mount: path=/var/lib/docker/overlay state=unmounted
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: remove files and dirs
+ file: name={{ item }} state=absent
+ with_items:
+ - "/var/lib/docker/"
+ - "/var/run/docker/"
+ - "/etc/systemd/system/docker.service"
+ - "/etc/systemd/system/docker.service.requires/"
+ - "/etc/systemd/system/docker.service.d/"
+ - "/etc/bash_completion.d/docker"
+ - "/usr/bin/docker"
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: remove files and dirs2
+ file: name={{ item }} state=absent
+ with_items:
+ - "/etc/cni/"
+ - "/run/flannel/"
+ - "/etc/calico/"
+ - "/var/run/calico/"
+ - "/var/lib/calico/"
+ - "/var/log/calico/"
+ - "/etc/cilium/"
+ - "/var/run/cilium/"
+ - "/sys/fs/bpf/tc/"
+ - "/var/lib/cni/"
+ - "/var/lib/kube-router/"
+ - "/opt/kube/kube-system/"
+
+ - name: cleanup iptables
+ shell: "iptables -F && iptables -X \
+ && iptables -F -t nat && iptables -X -t nat \
+ && iptables -F -t raw && iptables -X -t raw \
+ && iptables -F -t mangle && iptables -X -t mangle"
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: cleanup networks1
+ shell: "ip link del tunl0; \
+ ip link del flannel.1; \
+ ip link del cni0; \
+ ip link del mynet0; \
+ ip link del kube-bridge; \
+ ip link del dummy0; \
+ ip link del kube-ipvs0; \
+ ip link del cilium_net; \
+ ip link del cilium_vxlan"
+ ignore_errors: true
+
+ - name: cleanup networks2
+ shell: "ip link del docker0; \
+ systemctl restart networking; \
+ systemctl restart network"
+ ignore_errors: true
+ when: "'kubeasz' not in install_info.stdout"
+
+ - name: cleanup 'calico' routes
+ shell: "for rt in `ip route|grep bird|sed 's/blackhole//'|awk '{print $1}'`;do ip route del $rt;done;"
+ when: "CLUSTER_NETWORK == 'calico'"
+ ignore_errors: true
+
+ # 清理etcd 集群相关
+ - name: stop and disable etcd service
+ service:
+ name: etcd
+ state: stopped
+ enabled: no
+ ignore_errors: true
+
+ - name: 清理目录和文件
+ file: name={{ item }} state=absent
+ with_items:
+ - "/var/lib/etcd"
+ - "/etc/etcd/"
+ - "/backup/k8s"
+ - "/etc/systemd/system/etcd.service"
+
+ # 清理负载均衡相关
+ - name: stop keepalived service
+ shell: systemctl disable keepalived && systemctl stop keepalived
+ ignore_errors: true
+
+ - name: stop haproxy service
+ shell: systemctl disable haproxy && systemctl stop haproxy
+ ignore_errors: true
+
+ - name: 清理LB 配置文件目录
+ file: name={{ item }} state=absent
+ with_items:
+ - "/etc/haproxy"
+ - "/etc/keepalived"
+
+ # 清理其他
+ - name: stop and disable chrony in Ubuntu
+ service: name=chrony state=stopped enabled=no
+ ignore_errors: true
+ tags: rm_ntp
+ when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"
+
+ - name: stop and disable chronyd in CentOS/RedHat
+ service: name=chronyd state=stopped enabled=no
+ ignore_errors: true
+ tags: rm_ntp
+ when: ansible_distribution == "CentOS" or ansible_distribution == "RedHat"
+
+ - name: 清理证书目录和文件
+ file: name={{ item }} state=absent
+ with_items:
+ - "/etc/kubernetes/"
+ - "{{ ca_dir }}"
+ - "/root/.kube/"
+ - "/etc/docker/"
+
+ - name: 清理自动生成的PATH
+ lineinfile:
+ dest: ~/.bashrc
+ state: absent
+ regexp: '{{ item }}'
+ with_items:
+ - 'kubeasz'
+ - 'helm'
+ - 'kubectl completion'
+ delegate_to: "{{ NODE_TO_DEL }}"
+ run_once: true
+
+ # 执行kubectl delete(节点可能是kube-node节点)
+ - name: 执行kubectl delete(节点可能是kube-node节点)
+ shell: "{{ bin_dir }}/kubectl delete node {{ NODE_TO_DEL }}"
+ ignore_errors: true
+
+ # 删除 ansible hosts 中节点配置
+ - name: rm {{ NODE_TO_DEL }} in ansible hosts
+ lineinfile:
+ dest: "{{ base_dir }}/hosts"
+ state: absent
+ regexp: '{{ NODE_TO_DEL }}'
+ connection: local
diff --git a/tools/easzctl b/tools/easzctl
new file mode 100755
index 0000000..9dc20b5
--- /dev/null
+++ b/tools/easzctl
@@ -0,0 +1,191 @@
+#!/bin/bash
+#
+# This script can be used to manage k8s clusters. (developing)
+
+set -o nounset
+#set -o errexit
+#set -o xtrace
+
+function usage() {
+ cat <
+Commands:
+ add-node To add a kube-node(work node) to the k8s cluster
+ add-master To add a kube-master(master node) to the k8s cluster
+ add-etcd To add a etcd-node to the etcd cluster
+ del-etcd To delete a etcd-node from the etcd cluster
+ clean-node To clean a node, whatever role the node plays
+ help To display usage information
+
+Use "easzctl help " for more information about a given command.
+EOF
+}
+
+function process_cmd() {
+ echo -e "+---\033[33m$ACTION\033[0m---+ : $CMD"
+ $CMD || { echo -e "+---\033[31mAction failed\033[0m---+ : $CMD"; exit 1; }
+ echo -e "+---\033[32mAction successed\033[0m---+ : $CMD"
+}
+
+function add-node() {
+ # check new node's address regexp
+ [[ $1 =~ ^(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})(\.(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})){3}$ ]] || { echo "ERROR: Invalid ip address!"; exit 2; }
+
+ # check if the new node already exsited
+ sed -n '/^\[kube-master/,/^\[harbor/p' $BASEPATH/hosts|grep "^$1" && { echo "ERROR: node $1 already existed!"; exit 2; }
+
+ # add a node into 'kube-node' group
+ sed -i "/\[kube-node/a $1 NEW_NODE=yes" $BASEPATH/hosts
+
+ # check if playbook runs successfully
+ ansible-playbook $BASEPATH/tools/20.addnode.yml -e NODE_TO_ADD=$1 || { sed -i "/$1 NEW_NODE=yes/d" $BASEPATH/hosts; exit 2; }
+}
+
+function add-master() {
+ # check new master's address regexp
+ [[ $1 =~ ^(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})(\.(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})){3}$ ]] || { echo "ERROR: Invalid ip address!"; exit 2; }
+
+ # check if k8s with DPLOY_MODE='multi-master'
+ grep '^DEPLOY_MODE=multi-master' $BASEPATH/hosts || { echo "ERROR: only k8s with DPLOY_MODE='multi-master' can have master node added!"; exit 2; }
+
+ # check if the new master already exsited
+ sed -n '/^\[kube-master/,/^\[kube-node/p' $BASEPATH/hosts|grep "^$1" && { echo "ERROR: master $1 already existed!"; exit 2; }
+
+ # add a node into 'kube-master' group
+ sed -i "/\[kube-master/a $1 NEW_MASTER=yes" $BASEPATH/hosts
+
+ # check if playbook runs successfully
+ ansible-playbook $BASEPATH/tools/21.addmaster.yml -e NODE_TO_ADD=$1 || { sed -i "/$1 NEW_MASTER=yes/d" $BASEPATH/hosts; exit 2; }
+}
+
+function add-etcd() {
+ # check new node's address regexp
+ [[ $1 =~ ^(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})(\.(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})){3}$ ]] || { echo "ERROR: Invalid ip address!"; exit 2; }
+
+ # check if the new node already exsited
+ sed -n '/^\[etcd/,/^\[kube-master/p' $BASEPATH/hosts|grep "^$1" && { echo "ERROR: node $1 already existed!"; exit 2; }
+
+ # input an unique NODE_NAME of the node in etcd cluster
+ echo "Please input an UNIQUE name(string) for the new node: "
+ read NAME
+ sed -n '/^\[etcd/,/^\[kube-master/p' $BASEPATH/hosts|grep "$NAME" && { echo "ERROR: name [$NAME] already existed!"; exit 2; }
+
+ # add a node into 'kube-node' group
+ sed -i "/\[etcd/a $1 NODE_NAME=$NAME" $BASEPATH/hosts
+
+ # check if playbook runs successfully
+ ansible-playbook $BASEPATH/tools/19.addetcd.yml -e NODE_TO_ADD=$1 || { sed -i "/$1 NODE_NAME=$NAME/d" $BASEPATH/hosts; exit 2; }
+}
+
+function del-etcd() {
+ # check node's address regexp
+ [[ $1 =~ ^(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})(\.(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})){3}$ ]] || { echo "ERROR: Invalid ip address!"; exit 2; }
+
+ #
+ ansible-playbook $BASEPATH/tools/remove_etcd_node.yml -e ETCD_TO_DEL=$1
+}
+
+function clean-node() {
+ # check node's address regexp
+ [[ $1 =~ ^(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})(\.(2(5[0-5]{1}|[0-4][0-9]{1})|[0-1]?[0-9]{1,2})){3}$ ]] || { echo "ERROR: Invalid ip address!"; exit 2; }
+
+ #
+ ansible-playbook $BASEPATH/tools/clean_one_node.yml -e NODE_TO_DEL=$1
+}
+
+function help-info() {
+ case "$1" in
+ (add-node)
+ echo -e "Usage: easzctl add-node \n\nMore information please refer to 'docs/op/AddNode.md'"
+ ;;
+ (add-master)
+ echo -e "Usage: easzctl add-master \n\nMore information please refer to 'docs/op/AddMaster.md'"
+ ;;
+ (add-etcd)
+ echo -e "Usage: easzctl add-etcd \n\nMore information please refer to 'docs/op/op-etcd.md'"
+ ;;
+ (del-etcd)
+ echo -e "Usage: easzctl del-etcd \n\nMore information please refer to 'docs/op/op-etcd.md'"
+ ;;
+ (clean-node)
+ echo -e "Usage: easzctl clean-node \n\nMore information please refer to 'docs/op/clean_one_node.md'"
+ ;;
+ (*)
+ usage
+ exit 0
+ ;;
+ esac
+}
+
+function start() {
+ case "$1" in
+ (aio)
+ start-aio
+ ;;
+ (*)
+ exit 0
+ ;;
+ esac
+}
+
+function start-aio(){
+ [ -f "$BASEPATH/hosts" ] && { echo -e "ERROR: file $BASEPATH/hosts exists, checkout!\nRemove it if you really want to start an aio cluster"; exit 3; }
+ if [ ! -n "$KUBEASZ_DOCKER_HOST" ]; then
+ # easzctl runs in a host machine, get host's ip
+ HOST_IF=$(ip route|grep default|cut -d' ' -f5)
+ HOST_IP=$(ip a|grep $HOST_IF|awk 'NR==2{print $2}'|cut -d'/' -f1)
+ cp -f $BASEPATH/example/hosts.allinone.example.en $BASEPATH/hosts
+ sed -i "s/192.168.1.1/$HOST_IP/g" $BASEPATH/hosts
+ ansible-playbook $BASEPATH/90.setup.yml
+ else
+ # easzctl runs in a container
+ cp -f $BASEPATH/example/hosts.allinone.example.en $BASEPATH/hosts
+ sed -i "s/192.168.1.1/$KUBEASZ_DOCKER_HOST/g" $BASEPATH/hosts
+ ansible-playbook $BASEPATH/90.setup.yml
+ fi
+}
+
+###############################################################
+
+BASEPATH=/etc/ansible
+
+[ "$#" -gt 1 ] || { usage >&2; exit 2; }
+
+case "$1" in
+
+ (add-node)
+ ACTION="Action: add a k8s work node"
+ CMD="add-node $2"
+ ;;
+ (add-master)
+ ACTION="Action: add a k8s master node"
+ CMD="add-master $2"
+ ;;
+ (add-etcd)
+ ACTION="Action: add a etcd node"
+ CMD="add-etcd $2"
+ ;;
+ (del-etcd)
+ ACTION="Action: delete a etcd node"
+ CMD="del-etcd $2"
+ ;;
+ (clean-node)
+ ACTION="Action: clean a node"
+ CMD="clean-node $2"
+ ;;
+ (help)
+ help-info $2
+ exit 0
+ ;;
+ (start)
+ ACTION="Action: start an AllInOne cluster"
+ CMD="start $2"
+ ;;
+ (*)
+ usage
+ exit 0
+ ;;
+esac
+
+process_cmd
+
diff --git a/tools/kubeasz-docker b/tools/kubeasz-docker
new file mode 100755
index 0000000..b8bb262
--- /dev/null
+++ b/tools/kubeasz-docker
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+set -o nounset
+set -o errexit
+
+function usage() {
+ cat < To start a kubeasz(tagged) container
+ $0 clean To stop and remove all running containers
+
+EOF
+}
+
+function start_kubeasz_docker() {
+ # kubeasz docker version
+ KUBEASZ_DOCKER_VER=jmgao1983/kubeasz:$1
+ echo "VERSION $KUBEASZ_DOCKER_VER"
+
+ # init host dir
+ if [ -d "/etc/ansible/roles/" ];then
+ echo "==> aleady Initialized."
+ else
+ rm -rf /etc/ansible
+ echo "==> run a temporary container"
+ docker run -d --name temp_easz $KUBEASZ_DOCKER_VER
+ echo "==> init host kubeasz dir..."
+ docker cp temp_easz:/etc/ansible /etc/ansible
+ echo "==> stop&remove temporary container"
+ docker stop temp_easz
+ docker rm temp_easz
+ fi
+
+ # get host's IP
+ HOST_IF=$(ip route|grep default|cut -d' ' -f5)
+ HOST_IP=$(ip a|grep $HOST_IF|awk 'NR==2{print $2}'|cut -d'/' -f1)
+ echo "==> get host IP: $HOST_IP"
+
+ # run kubeasz docker container
+ echo "==> run kubeasz in a container"
+ docker run --detach \
+ --name kubeasz \
+ --restart always \
+ --env KUBEASZ_DOCKER_HOST=$HOST_IP \
+ --volume /etc/ansible:/etc/ansible \
+ --volume /root/.kube:/root/.kube \
+ --volume /root/.ssh/id_rsa:/root/.ssh/id_rsa:ro \
+ --volume /root/.ssh/id_rsa.pub:/root/.ssh/id_rsa.pub:ro \
+ --volume /root/.ssh/known_hosts:/root/.ssh/known_hosts:ro \
+ $KUBEASZ_DOCKER_VER
+}
+
+function clean_container() {
+ echo "==> clean all running containers"
+ docker ps -a|awk 'NR>1{print $1}'|xargs docker rm -f
+}
+
+[ "$#" -gt 0 ] || { usage >&2; exit 2; }
+
+case "$1" in
+
+ (start)
+ [ "$#" -gt 1 ] || { usage >&2; exit 2; }
+ start_kubeasz_docker $2
+ ;;
+ (clean)
+ clean_container
+ ;;
+ (*)
+ usage
+ exit 0
+ ;;
+esac
diff --git a/tools/remove_etcd_node.yml b/tools/remove_etcd_node.yml
new file mode 100644
index 0000000..b27a2e0
--- /dev/null
+++ b/tools/remove_etcd_node.yml
@@ -0,0 +1,52 @@
+# remove a etcd member
+- hosts: deploy
+ vars_prompt:
+ - name: "ETCD_TO_DEL"
+ prompt: "which etcd node is about to be deleted?(e.g 192.168.1.1)"
+ private: no
+ confirm: yes
+ tasks:
+ - name: fail info1
+ fail: msg="{{ ETCD_TO_DEL }} is NOT a member of etcd cluster!"
+ when: "ETCD_TO_DEL not in groups['etcd']"
+
+ - name: fail info2
+ fail: msg="you CAN NOT delete the last member of etcd cluster!"
+ when: "groups['etcd']|length < 2"
+
+ - block:
+ - name: get ID of etcd node to delete
+ shell: "ETCDCTL_API=3 {{ bin_dir }}/etcdctl member list|grep {{ ETCD_TO_DEL }}:2380|cut -d',' -f1"
+ register: ETCD_ID
+ delegate_to: "{{ groups.etcd[0] }}"
+
+ - name: get NAME of etcd node to delete
+ shell: "ETCDCTL_API=3 {{ bin_dir }}/etcdctl member list|grep {{ ETCD_TO_DEL }}:2380|cut -d' ' -f3|cut -d',' -f1"
+ register: ETCD_NAME
+ delegate_to: "{{ groups.etcd[0] }}"
+
+ - name: rm etcd's node in hosts
+ lineinfile:
+ dest: "{{ base_dir }}/hosts"
+ state: absent
+ regexp: '{{ ETCD_NAME.stdout }}'
+ connection: local
+ when: "ETCD_NAME.stdout != ''"
+
+ - name: delete a etcd member
+ shell: "ETCDCTL_API=3 {{ bin_dir }}/etcdctl member remove {{ ETCD_ID.stdout }}"
+ delegate_to: "{{ groups.etcd[0] }}"
+ when: "ETCD_ID.stdout != ''"
+
+ - name: rm data of the deleted etcd node
+ file: name=/var/lib/etcd state=absent
+ delegate_to: "{{ ETCD_TO_DEL }}"
+ when: "ETCD_ID.stdout != ''"
+
+ - name: reconfig and restart the etcd cluster
+ shell: "ansible-playbook /etc/ansible/02.etcd.yml > /tmp/ansible-playbook.log 2>&1"
+ connection: local
+ when: "ETCD_ID.stdout != ''"
+ run_once: true
+ # 满足条件才进行删除
+ when: "groups['etcd']|length > 1 and ETCD_TO_DEL in groups['etcd']"
diff --git a/tools/yc-ssh-key-copy.sh b/tools/yc-ssh-key-copy.sh
new file mode 100755
index 0000000..af3ee47
--- /dev/null
+++ b/tools/yc-ssh-key-copy.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+# 此脚本为批量部署服务器ssh key使用
+
+#set -x
+
+# check args count
+if test $# -ne 3; then
+ echo -e "\nUsage: $0 < hosts file > < username > < password >\n"
+ exit 1
+fi
+
+# check hosts file
+hosts_file=$1
+if ! test -e $hosts_file; then
+ echo "[ERROR]: Can't find hosts file"
+ exit 1
+fi
+
+username=$2
+password=$3
+
+# check sshkey file
+sshkey_file=~/.ssh/id_rsa.pub
+if ! test -e $sshkey_file; then
+ expect -c "
+ spawn ssh-keygen -t rsa
+ expect \"Enter*\" { send \"\n\"; exp_continue; }
+ "
+fi
+
+# get hosts list
+hosts=$(ansible -i $hosts_file all --list-hosts | awk 'NR>1')
+echo "======================================================================="
+echo "hosts: "
+echo "$hosts"
+echo "======================================================================="
+
+ssh_key_copy()
+{
+ # delete history
+ sed "/$1/d" -i ~/.ssh/known_hosts
+
+ # start copy
+ expect -c "
+ set timeout 100
+ spawn ssh-copy-id $username@$1
+ expect {
+ \"yes/no\" { send \"yes\n\"; exp_continue; }
+ \"password\" { send \"$password\n\"; }
+ \"already exist on the remote system\" { exit 1; }
+ }
+ expect eof
+ "
+}
+
+# auto sshkey pair
+for host in $hosts; do
+ echo "======================================================================="
+
+ # check network
+ ping -i 0.2 -c 3 -W 1 $host >& /dev/null
+ if test $? -ne 0; then
+ echo "[ERROR]: Can't connect $host"
+ exit 1
+ fi
+
+ cat /etc/hosts | grep -v '^#' | grep $host >& /dev/null
+ if test $? -eq 0; then
+ hostaddr=$(cat /etc/hosts | grep -v '^#' | grep $host | awk '{print $1}')
+ hostname=$(cat /etc/hosts | grep -v '^#' | grep $host | awk '{print $2}')
+
+ ssh_key_copy $hostaddr
+ ssh_key_copy $hostname
+ else
+ ssh_key_copy $host
+ fi
+
+ echo ""
+done