├── .github └── CODEOWNERS ├── .gitignore ├── LICENSE ├── README.md ├── Vagrantfile ├── bootstrap ├── bootstrap_test ├── app_ctl.py ├── config.py ├── config.yaml ├── conftest.py ├── distribute_ssh_key.yaml ├── host_vars │ └── test-nodes ├── pip-req.txt ├── prepare_demo_images.sh ├── test_bootstrap.py └── test_ipaddr.py ├── download-consul.sh ├── playbooks ├── bootstrap-hosts ├── bootstrap.yaml ├── cluster ├── dep-graph.py ├── dep-graph2.py ├── host_vars │ └── bootstrap-node ├── role.yaml ├── roles │ ├── ansible_plugins │ │ ├── files │ │ │ └── plugins │ │ │ │ └── callback │ │ │ │ └── timestamp.py │ │ └── tasks │ │ │ └── main.yaml │ ├── backupd │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── backupd.service.j2 │ ├── binary │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-binary-stop │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-console-deploy │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-console-start │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-console-stop │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-console │ │ └── meta │ │ │ └── main.yaml │ ├── bootstrap-docker │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-etcd │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── etcd.cron.j2 │ ├── bootstrap-firewall │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-images │ │ └── vars │ │ │ └── main.yaml │ ├── bootstrap-layer0 │ │ └── meta │ │ │ └── main.yaml │ ├── bootstrap-layer1 │ │ └── meta │ │ │ └── main.yaml │ ├── bootstrap-registry-push │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-registry-start │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ ├── main.yaml │ │ │ └── start.yaml │ ├── bootstrap-registry-stop │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-registry │ │ └── meta │ │ │ └── main.yaml │ ├── bootstrap-tinydns │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-webrouter-start │ │ ├── files │ │ │ └── nginx │ │ │ │ ├── default.conf │ │ │ │ ├── nginx.conf │ │ │ │ └── proxy.conf │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── nginx │ │ │ ├── console.conf.j2 │ │ │ └── registry.conf.j2 │ ├── bootstrap-webrouter-stop │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── bootstrap-webrouter │ │ └── meta │ │ │ └── main.yaml │ ├── bootstrap │ │ └── meta │ │ │ └── main.yaml │ ├── calico │ │ ├── files │ │ │ ├── calico.conf │ │ │ ├── calico │ │ │ │ ├── confd │ │ │ │ │ ├── conf.d │ │ │ │ │ │ ├── bird.toml.toml │ │ │ │ │ │ ├── bird6.toml.toml │ │ │ │ │ │ ├── bird6_ipam.toml │ │ │ │ │ │ ├── custom_filters.toml │ │ │ │ │ │ ├── custom_filters6.toml │ │ │ │ │ │ └── tunl-ip.toml │ │ │ │ │ ├── config │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── templates │ │ │ │ │ │ ├── bird6_ipam.cfg.template │ │ │ │ │ │ ├── bird_aggr.cfg.template │ │ │ │ │ │ ├── custom_filters.cfg.template │ │ │ │ │ │ ├── custom_filters6.cfg.template │ │ │ │ │ │ └── tunl-ip.template │ │ │ │ └── profile.yml │ │ │ └── service │ │ │ │ ├── calico-bird.service │ │ │ │ ├── calico-bird6.service │ │ │ │ ├── calico-felix.service │ │ │ │ └── calico-libnetwork.service │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── bird.cfg.mesh.template.j2 │ │ │ ├── bird.cfg.no-mesh.template.j2 │ │ │ ├── bird.toml.template.j2 │ │ │ ├── bird6.cfg.mesh.template.j2 │ │ │ ├── bird6.cfg.no-mesh.template.j2 │ │ │ ├── bird6.toml.template.j2 │ │ │ ├── bird6_aggr.toml.j2 │ │ │ ├── bird_aggr.toml.j2 │ │ │ ├── bird_ipam.cfg.template.j2 │ │ │ ├── bird_ipam.toml.j2 │ │ │ ├── calico-confd.service.j2 │ │ │ ├── calico.env.j2 │ │ │ ├── calicoctl.cfg.j2 │ │ │ ├── felix.cfg.j2 │ │ │ └── ippool.yml.j2 │ ├── ceph-fuse-new │ │ ├── files │ │ │ └── plugins │ │ │ │ └── ceph_mon.py │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── ceph.client.lain.keyring.j2 │ │ │ └── ceph.conf.j2 │ ├── ceph-fuse │ │ ├── files │ │ │ └── plugins │ │ │ │ └── ceph_mon.py │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── ceph.client.lain.keyring.j2 │ │ │ ├── ceph.conf.j2 │ │ │ └── cephmon.service.j2 │ ├── collectd │ │ ├── files │ │ │ ├── plugins │ │ │ │ └── lain │ │ │ │ │ ├── cluster_monitor.py │ │ │ │ │ ├── deployd_monitor.py │ │ │ │ │ ├── docker_daemon_monitor.py │ │ │ │ │ ├── lain_docker.py │ │ │ │ │ ├── lainlet_monitor.py │ │ │ │ │ ├── node_monitor.py │ │ │ │ │ ├── plugin.py │ │ │ │ │ ├── procutils.py │ │ │ │ │ └── rebellion_monitor.py │ │ │ └── types │ │ │ │ └── lain.db │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── collectd.conf.j2 │ │ │ ├── docker_daemon_monitor.conf.j2 │ │ │ ├── lain.conf.j2 │ │ │ ├── node_monitor.conf.j2 │ │ │ └── rebellion_monitor.conf.j2 │ ├── config │ │ └── defaults │ │ │ └── main.yaml │ ├── console-deploy │ │ └── tasks │ │ │ └── main.yaml │ ├── console │ │ └── tasks │ │ │ └── main.yaml │ ├── consul │ │ ├── files │ │ │ └── consul.service │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── consul.json.j2 │ ├── cronjob │ │ ├── files │ │ │ └── clean_lainnode_image.py │ │ └── tasks │ │ │ └── main.yml │ ├── deployd │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── deployd.service.j2 │ ├── docker-netstat │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── docker-netstat.service.j2 │ ├── docker-upgrade │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── docker-version │ │ ├── defaults │ │ │ └── main.yaml │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ ├── devicemapper.yaml │ │ │ └── main.yaml │ │ └── templates │ │ │ └── docker.j2 │ ├── docker │ │ ├── files │ │ │ ├── bootstrap-ubuntu-docker.sh │ │ │ ├── docker-enter │ │ │ └── docker-enter-comp │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ ├── devicemapper.yaml │ │ │ └── main.yaml │ │ └── templates │ │ │ └── daemon.json.j2 │ ├── drift-warm-up │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── drift │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── etcd-backup │ │ ├── files │ │ │ └── plugins │ │ │ │ └── lain │ │ │ │ └── etcd_backup │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── etcd_backup.service.j2 │ ├── etcd-moosefs │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ ├── build.yaml │ │ │ └── main.yaml │ │ └── templates │ │ │ └── data-lain-etcd_backup.mount.j2 │ ├── etcd │ │ ├── files │ │ │ └── etcdctl_comp │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ ├── etcd-reset.yaml │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── etcd.env.j2 │ │ │ └── etcd.service.j2 │ ├── firewall │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── iptables.save.j2 │ │ │ └── iptables.sh.j2 │ ├── images │ │ ├── meta │ │ │ └── main.yml │ │ └── tasks │ │ │ └── main.yml │ ├── lainlet │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── lainlet.cron.j2 │ │ │ └── lainlet.service.j2 │ ├── libraries │ │ └── library │ │ │ ├── docker_check_images.py │ │ │ ├── docker_inspect.py │ │ │ ├── docker_pull_image.py │ │ │ ├── docker_push.py │ │ │ ├── etcd_prepare_update.py │ │ │ ├── etcd_set_key.py │ │ │ ├── get_app_proc_ip.py │ │ │ ├── get_virtual_ip.py │ │ │ ├── image_clean.py │ │ │ ├── inspect_docker_network.py │ │ │ ├── post_json.py │ │ │ ├── set_config_domain.py │ │ │ ├── set_tinydns_domain.py │ │ │ ├── set_virtual_ip.py │ │ │ └── set_virtual_ip_key.py │ ├── lvault-app │ │ └── meta │ │ │ └── main.yaml │ ├── manager │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── moosefs-build │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ ├── chunkserver.yaml │ │ │ ├── main.yaml │ │ │ ├── master.yaml │ │ │ └── metalogger.yaml │ │ └── templates │ │ │ ├── config │ │ │ ├── mfschunkserver.cfg.j2 │ │ │ ├── mfshdd.cfg.j2 │ │ │ ├── mfsmaster.cfg.j2 │ │ │ ├── mfsmetalogger.cfg.j2 │ │ │ └── mfsmount.cfg.j2 │ │ │ └── service │ │ │ ├── mfscgiserv.service.j2 │ │ │ ├── mfschunkserver.service.j2 │ │ │ ├── mfsmaster.service.j2 │ │ │ └── mfsmetalogger.service.j2 │ ├── moosefs │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── mysql │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── network-recover │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── networkd-preupgrade │ │ └── tasks │ │ │ └── main.yaml │ ├── networkd-rollback │ │ └── tasks │ │ │ └── main.yaml │ ├── networkd-update │ │ └── tasks │ │ │ └── main.yaml │ ├── networkd-upgrade │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── networkd.service.j2 │ ├── networkd │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── networkd.service.j2 │ ├── node-change-labels │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── docker-daemon.json.j2 │ ├── node-clean │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ ├── image-clean.yaml │ │ │ ├── log-clean.yaml │ │ │ └── main.yaml │ ├── node-disable-log-driver │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── docker-daemon.json.j2 │ ├── node │ │ └── meta │ │ │ └── main.yaml │ ├── os-dependent-vars │ │ ├── tasks │ │ │ └── main.yaml │ │ └── vars │ │ │ ├── CentOS.yml │ │ │ └── Ubuntu.yml │ ├── packages │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── prepare │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── rebellion-upgrade-2.0.x-to-v2.3.0 │ │ ├── files │ │ │ └── clean_logs.py │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── rebellion.service.j2 │ ├── rebellion-upgrade │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ └── rebellion.service.j2 │ ├── rebellion │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── 10-docker-rsyslog.conf.j2 │ │ │ └── rebellion.service.j2 │ ├── registry-moosefs │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ ├── build.yaml │ │ │ └── main.yaml │ │ └── templates │ │ │ └── var-lib-registry.mount.j2 │ ├── registry │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ ├── main.yaml │ │ │ └── push.yml │ │ └── templates │ │ │ ├── registry.j2 │ │ │ └── var-lib-registry.mount.j2 │ ├── remove-node │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── rsync │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── rsyncd.conf.j2 │ │ │ ├── rsyncd.secrets.j2 │ │ │ └── rsyncd.service.j2 │ ├── rsyslog-relocate │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── syslog-messages.conf.j2 │ │ │ └── syslog-messages.j2 │ ├── rsyslog │ │ ├── files │ │ │ └── rsyslog.conf │ │ ├── handlers │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ ├── ssl │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ ├── ca.yaml │ │ │ └── main.yaml │ ├── swarm-manage │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ ├── main.yaml │ │ │ └── swarm-manager.yaml │ │ └── templates │ │ │ ├── swarm-agent.service.j2 │ │ │ └── swarm-manager.service.j2 │ ├── swarm-upgrade │ │ ├── handlers │ │ │ └── main.yaml │ │ ├── meta │ │ │ └── main.yaml │ │ ├── tasks │ │ │ └── main.yaml │ │ └── templates │ │ │ ├── swarm-agent.service.j2 │ │ │ └── swarm-manager.service.j2 │ ├── swarm │ │ └── meta │ │ │ └── main.yaml │ ├── systemd │ │ ├── files │ │ │ └── journald.conf.d │ │ │ │ └── lain.conf │ │ └── tasks │ │ │ └── main.yaml │ ├── webrouter-start │ │ ├── meta │ │ │ └── main.yaml │ │ └── tasks │ │ │ └── main.yaml │ └── webrouter │ │ ├── files │ │ └── nginx │ │ │ ├── default.conf │ │ │ ├── nginx.conf │ │ │ └── proxy.conf │ │ └── tasks │ │ └── main.yaml └── site.yaml └── prepare.sh /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://help.github.com/articles/about-codeowners/ 2 | # 3 | * @bibaijin 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .DS_Store 4 | *.swp 5 | *.pyc 6 | env/ 7 | lain_box/centos7 8 | lain_box/lain.box 9 | .ropeproject/ 10 | examples/hello/Dockerfile 11 | playbooks/roles/binary/files/* 12 | */**/.gitignore 13 | **/.coverage 14 | /site/ 15 | **/unittest.xml 16 | **/htmlcov 17 | *.gz 18 | *.bz2 19 | *.tar 20 | **/build 21 | **/dist 22 | **/*.egg-info 23 | venv/ 24 | playbooks/bootstrap.retry 25 | playbooks/roles/calico-upgrade/files/bin 26 | playbooks/roles/calico/files/bin 27 | playbooks/roles/etcd/files/etcd 28 | playbooks/roles/etcd/files/etcdctl 29 | playbooks/roles/networkd-upgrade/files 30 | playbooks/roles/networkd/files 31 | playbooks/roles/lainlet/files 32 | playbooks/roles/deployd/files 33 | playbooks/roles/consul/files/bin 34 | playbooks/roles/etcd2consul/files/bin 35 | playbooks/roles/docker-netstat/files/bin 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 LAIN Cloud 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LAIN 2 | 3 | [![MIT license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT) 4 | [![Gitter](https://badges.gitter.im/gitterHQ/gitter.svg)](https://gitter.im/laincloud/opensource) 5 | [![Throughput Graph](https://graphs.waffle.io/laincloud/lain/throughput.svg)](https://waffle.io/laincloud/lain/metrics/throughput) 6 | 7 | Lain 是一个基于 Docker 的 PaaS 系统。 8 | 9 | 其面向技术栈多样寻求高效运维方案的高速发展中的组织,DevOps 人力缺乏的 startup ,个人开发者。 10 | 11 | 统一高效的开发工作流,降低应用运维复杂度;在 IaaS / 私有 IDC 裸机的基础上直接提供应用开发,集成,部署,运维的一揽子解决方案。 12 | 13 | 设计目标包括但不限于: 14 | 15 | - 降低系统管理复杂度 16 | - 简化服务的部署管理 17 | - 优化基础服务的调配 18 | - 提高资源的使用效率 19 | - 统一开发测试生产三环境 20 | - 持续交付工作流的良好支持 21 | 22 | ## Latest Release 23 | 24 | 最新版是2.1.1。 25 | 26 | - [下载](https://github.com/laincloud/lain/archive/v2.1.1.tar.gz) 27 | - [Release note](https://github.com/laincloud/lain/releases/tag/v2.1.1) 28 | 29 | ## Quick Start 30 | 31 | ```shell 32 | curl -fsSL https://github.com/laincloud/lain/archive/v2.1.1.tar.gz | tar xf - 33 | cd lain-2.1.1 34 | vagrant up 35 | # Config DNS in local shell 36 | sudo bash -c 'echo "192.168.77.201 console.lain.local" >> /etc/hosts' 37 | ``` 38 | 39 | 初始化完成后即可在浏览器访问console: 40 | ``` 41 | http://console.lain.local 42 | ``` 43 | 44 | 完整的文档在[这里](https://laincloud.gitbooks.io/white-paper/content/),其中: 45 | - [Install](https://laincloud.gitbooks.io/white-paper/install/cluster.html) 展示了如何从头开始构建 Lain 集群 46 | - [LAIN App Demo](https://laincloud.gitbooks.io/white-paper/tutorial/first-lain-app.html) 展示了如何在 Lain 集群上部署应用 47 | 48 | ## Contributors 49 | 50 | - @[Qiangning Hong](https://github.com/hongqn) 51 | - @[Jia Mi](https://github.com/mijia) 52 | - @[flex](https://github.com/frostynova) 53 | - @[Tachikoma](https://github.com/sunyi00) 54 | - @[cloudfly](https://github.com/cloudfly) 55 | - @[BaiJian](https://github.com/ericpai) 56 | - @[Pan Li](https://github.com/panli889) 57 | - @[Meng Wenbin](https://github.com/supermeng) 58 | - @[chaoyiwang](https://github.com/wchaoyi) 59 | - @[Zhuoyun Wei](https://github.com/wzyboy) 60 | - @[Xu Tao](https://github.com/xtao) 61 | - @[Chang Cheng](https://github.com/uronce-cc) 62 | - @[Xu Yunnan](https://github.com/XuYunnan) 63 | - @[Zhang Kai](https://github.com/bibaijin) 64 | - @[Xu Zhuofu](https://github.com/ipush) 65 | - @[Luo Libin](https://github.com/onlymellb) 66 | 67 | ## LICENSE 68 | 69 | Lain is licensed under the [MIT license](LICENSE). 70 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | os_type = (ENV['OS_TYPE'] || "centos").downcase 2 | 3 | case os_type 4 | when "centos" 5 | Vagrant.configure(2) do |config| 6 | (1..3).each do |i| 7 | nodename = "node#{i}" 8 | config.vm.define nodename, primary: i == 1, autostart: i == 1 do |node| 9 | node.vm.box = "laincloud/centos-lain" 10 | node.vm.hostname = nodename 11 | node.vm.provider "virtualbox" do |v| 12 | v.memory = i == 1 ? 1536 : 768 13 | end 14 | if i == 1 15 | node.vm.provision "shell", 16 | inline: "sudo /vagrant/bootstrap "\ 17 | "-m https://l2ohopf9.mirror.aliyuncs.com "\ 18 | "-r docker.io/laincloud --vip=192.168.77.201" 19 | end 20 | node.vm.network "private_network", ip: "192.168.77.2#{i}" 21 | end 22 | end 23 | end 24 | 25 | when "ubuntu" 26 | ENV["LC_ALL"] = "C" 27 | 28 | unless Vagrant.has_plugin?("vagrant-disksize") 29 | puts "please install disksize plugin first." 30 | puts "cmd: vagrant plugin install vagrant-disksize" 31 | raise "vagrant-disksize is not isntalled." 32 | end 33 | 34 | Vagrant.configure(2) do |config| 35 | (1..3).each do |i| 36 | nodename = "node#{i}" 37 | config.vm.define nodename, primary: i == 1, autostart: i == 1 do |node| 38 | node.vm.box = "ubuntu/xenial64" 39 | node.vm.hostname = nodename 40 | node.disksize.size = '30GB' 41 | node.vm.provider "virtualbox" do |v| 42 | v.memory = i == 1 ? 1536 : 768 43 | end 44 | 45 | if i == 1 46 | node.vm.provision "shell", 47 | inline: "sudo python3 /vagrant/bootstrap "\ 48 | "-m https://l2ohopf9.mirror.aliyuncs.com "\ 49 | "-r docker.io/laincloud --vip=192.168.77.201" 50 | else 51 | node.vm.provision "shell", 52 | inline: "sed -i 's/^PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config && "\ 53 | "systemctl restart sshd &&" \ 54 | "echo 'root:vagrant' | chpasswd &&" \ 55 | "sed -i s/archive.ubuntu.com/mirrors.ustc.edu.cn/g /etc/apt/sources.list && " \ 56 | "apt update && apt install -y python" 57 | end 58 | node.vm.network "private_network", ip: "192.168.77.2#{i}" 59 | end 60 | end 61 | end 62 | else 63 | puts "invalid os type" 64 | end 65 | -------------------------------------------------------------------------------- /bootstrap_test/app_ctl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: UTF-8 -*- 3 | 4 | import requests 5 | from config import CONFIG 6 | 7 | 8 | def reposit(app_name): 9 | url = "http://" + CONFIG.vip + CONFIG.console_api_repos 10 | payload = {"appname": app_name} 11 | headers = { 12 | "Host": CONFIG.console_hostname, 13 | "Content-Type": "application/json" 14 | } 15 | return requests.post(url, json=payload, headers=headers) 16 | 17 | 18 | def deploy(app_name): 19 | url = "http://" + CONFIG.vip + CONFIG.console_api_apps 20 | payload = {"appname": app_name} 21 | headers = { 22 | "Host": CONFIG.console_hostname, 23 | "Content-Type": "application/json" 24 | } 25 | return requests.post(url, json=payload, headers=headers) 26 | 27 | 28 | def delete(app_name): 29 | url = "http://" + CONFIG.vip + CONFIG.console_api_apps + app_name + "/" 30 | headers = {"Host": CONFIG.console_hostname} 31 | return requests.delete(url, headers=headers) 32 | 33 | 34 | def scale(app_name, proc_name, num_instances): 35 | url = "http://" + CONFIG.vip + CONFIG.console_api_apps + app_name + \ 36 | "/procs/" + proc_name + "/" 37 | payload = {"num_instances": num_instances} 38 | headers = { 39 | "Host": CONFIG.console_hostname, 40 | "Content-Type": "application/json" 41 | } 42 | return requests.patch(url, json=payload, headers=headers) 43 | 44 | 45 | def get_proc_info(app_name, proc_name): 46 | url = "http://" + CONFIG.vip + CONFIG.console_api_apps + app_name + \ 47 | "/procs/" + proc_name + "/" 48 | headers = {"Host": CONFIG.console_hostname} 49 | return requests.get(url, headers=headers) 50 | -------------------------------------------------------------------------------- /bootstrap_test/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: UTF-8 -*- 3 | 4 | import os 5 | import yaml 6 | 7 | 8 | class Config(object): 9 | def __init__(self): 10 | config_file_path = os.path.join(os.path.dirname(__file__), 11 | 'config.yaml') 12 | with open(config_file_path, 'r') as f: 13 | self._config = yaml.safe_load(f) 14 | 15 | @property 16 | def vip(self): 17 | return self._config["vip"] 18 | 19 | @property 20 | def domain(self): 21 | return self._config["domain"] 22 | 23 | @property 24 | def console_hostname(self): 25 | return "console." + self.domain 26 | 27 | @property 28 | def registry_hostname(self): 29 | return "registry." + self.domain 30 | 31 | @property 32 | def console_api_repos(self): 33 | return self._config["console_api"]["repos"] 34 | 35 | @property 36 | def console_api_apps(self): 37 | return self._config["console_api"]["apps"] 38 | 39 | @property 40 | def ipaddr(self): 41 | return self._config["ipaddr"] 42 | 43 | @property 44 | def ipaddr_service_appname(self): 45 | return self.ipaddr["service"]["appname"] 46 | 47 | @property 48 | def ipaddr_resource_appname(self): 49 | return self.ipaddr["resource"]["appname"] 50 | 51 | @property 52 | def ipaddr_client_appname(self): 53 | return self.ipaddr["client"]["appname"] 54 | 55 | @property 56 | def ipaddr_client_procname(self): 57 | return self.ipaddr["client"]["procname"] 58 | 59 | @property 60 | def ipaddr_client_num_instances(self): 61 | return self.ipaddr["client"]["num_instances"] 62 | 63 | @property 64 | def ipaddr_client_hostname(self): 65 | return self.ipaddr_client_appname + "." + self.domain 66 | 67 | 68 | CONFIG = Config() 69 | -------------------------------------------------------------------------------- /bootstrap_test/config.yaml: -------------------------------------------------------------------------------- 1 | vip: 192.168.77.201 2 | domain: lain.local 3 | console_api: 4 | repos: /api/v1/repos/ 5 | apps: /api/v1/apps/ 6 | 7 | ipaddr: 8 | resource: 9 | appname: ipaddr-resource 10 | service: 11 | appname: ipaddr-service 12 | client: 13 | appname: ipaddr-client 14 | procname: web 15 | num_instances: 2 16 | -------------------------------------------------------------------------------- /bootstrap_test/distribute_ssh_key.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | tasks: 4 | - name: install sshpass 5 | package: name=sshpass state=latest 6 | 7 | - hosts: test_nodes 8 | gather_facts: no 9 | tasks: 10 | - name: examine whether new nodes in known_hosts 11 | local_action: command grep '{{ ansible_ssh_host }}' /root/.ssh/known_hosts 12 | register: exists 13 | ignore_errors: True 14 | changed_when: False 15 | - name: collect the public keys of new nodes 16 | local_action: shell ssh-keyscan -p {{ ansible_ssh_port|default(22) }} {{ ansible_ssh_host }} 17 | register: result 18 | when: exists|failed 19 | - name: add the public keys of new nodes to known_hosts 20 | local_action: known_hosts path='/root/.ssh/known_hosts' host={{ ansible_ssh_host }} key="{{ result.stdout }}" 21 | when: exists|failed 22 | 23 | - hosts: test_nodes 24 | tasks: 25 | - name: upload public key to new nodes 26 | authorized_key: user=root key="{{ lookup('file', '/root/.ssh/lain.pub') }}" 27 | -------------------------------------------------------------------------------- /bootstrap_test/host_vars/test-nodes: -------------------------------------------------------------------------------- 1 | [test_nodes] 2 | node2 ansible_ssh_port=22 ansible_ssh_host=192.168.77.22 ansible_ssh_pass=vagrant 3 | node3 ansible_ssh_port=22 ansible_ssh_host=192.168.77.23 ansible_ssh_pass=vagrant 4 | -------------------------------------------------------------------------------- /bootstrap_test/pip-req.txt: -------------------------------------------------------------------------------- 1 | pytest==3.0.5 2 | requests==2.12.4 3 | subprocess32==3.2.6 4 | pyyaml==3.12 5 | retrying==1.3.3 6 | -------------------------------------------------------------------------------- /bootstrap_test/prepare_demo_images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DOMAIN=$1 4 | 5 | [[ -n "$DOMAIN" ]] || DOMAIN=lain.local 6 | 7 | BOOTSTRAP_REG=$2 8 | 9 | [[ -n "$BOOTSTRAP_REG" ]] || BOOTSTRAP_REG=registry.aliyuncs.com 10 | 11 | 12 | IMAGES=( 13 | ipaddr-service:meta-1462790282-60f77d22799d8823ef771faef97897d60ca9c4b1 14 | ipaddr-service:release-1462790282-60f77d22799d8823ef771faef97897d60ca9c4b1 15 | ipaddr-resource:meta-1462784153-944220ca13e9aae08412875990686e18b71bff9e 16 | ipaddr-resource:release-1462784153-944220ca13e9aae08412875990686e18b71bff9e 17 | ipaddr-client:meta-1481187933-05da018887e967144f7d481b7ef160cb173973de 18 | ipaddr-client:release-1481187933-05da018887e967144f7d481b7ef160cb173973de 19 | ); 20 | 21 | 22 | function pull_push() 23 | { 24 | IMAGE=$1 25 | BOOTSTRAP_IMAGE="${BOOTSTRAP_REG}/laincloud/${IMAGE}" 26 | TARGET_IMAGE="registry.${DOMAIN}/${IMAGE}" 27 | sudo docker pull ${BOOTSTRAP_IMAGE} 28 | sudo docker tag ${BOOTSTRAP_IMAGE} ${TARGET_IMAGE} 29 | sudo docker push ${TARGET_IMAGE} 30 | sudo docker rmi ${BOOTSTRAP_IMAGE} 31 | } 32 | 33 | for i in "${IMAGES[@]}" 34 | do 35 | pull_push $i 36 | done 37 | -------------------------------------------------------------------------------- /bootstrap_test/test_bootstrap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: UTF-8 -*- 3 | 4 | import requests 5 | 6 | from config import CONFIG 7 | 8 | 9 | def test_registry_service(bootstrap): 10 | # verify return 200 11 | url = "http://" + CONFIG.vip + "/v2/" 12 | headers = {"Host": CONFIG.registry_hostname} 13 | req = requests.get(url, headers=headers) 14 | assert req.status_code == 200 15 | 16 | # verify there is something in registry 17 | url += "_catalog" 18 | req = requests.get(url, headers=headers) 19 | assert len(req.json()["repositories"]) > 0 20 | 21 | 22 | def test_console_service(bootstrap): 23 | url = "http://" + CONFIG.vip + CONFIG.console_api_apps + "console/" 24 | headers = {"Host": CONFIG.console_hostname} 25 | req = requests.get(url, headers=headers) 26 | assert req.status_code == 200 27 | -------------------------------------------------------------------------------- /bootstrap_test/test_ipaddr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: UTF-8 -*- 3 | 4 | import requests 5 | import app_ctl 6 | from config import CONFIG 7 | from retrying import retry 8 | 9 | @retry(stop_max_attempt_number=20, wait_fixed=10000) 10 | def test_client_is_working(deploy_ipaddr): 11 | url = "http://" + CONFIG.vip 12 | headers = {"Host": CONFIG.ipaddr_client_hostname} 13 | req = requests.get(url, headers=headers) 14 | assert req.status_code == 200 15 | 16 | @retry(stop_max_attempt_number=20, wait_fixed=10000) 17 | def test_client_is_scaled(scale_ipaddr_client): 18 | req = app_ctl.get_proc_info(CONFIG.ipaddr_client_appname, 19 | CONFIG.ipaddr_client_procname) 20 | assert len(req.json()['proc']['pods']) == CONFIG.ipaddr_client_num_instances 21 | -------------------------------------------------------------------------------- /download-consul.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p playbooks/roles/consul/files/bin 4 | 5 | mkdir -p playbooks/roles/etcd2consul/files/bin 6 | 7 | wget -c https://releases.hashicorp.com/consul/0.8.5/consul_0.8.5_linux_amd64.zip -O playbooks/roles/consul/files/bin/consul_0.8.5_linux_amd64.zip 8 | 9 | wget -c https://github.com/laincloud/etcd2consul/releases/download/v0.0.1/etcd2consul.xz -O playbooks/roles/etcd2consul/files/bin/etcd2consul.xz 10 | 11 | unzip -o playbooks/roles/consul/files/bin/consul_0.8.5_linux_amd64.zip -d playbooks/roles/consul/files/bin 12 | 13 | unxz -kf playbooks/roles/etcd2consul/files/bin/etcd2consul.xz 14 | -------------------------------------------------------------------------------- /playbooks/bootstrap-hosts: -------------------------------------------------------------------------------- 1 | bootstrap-node ansible_connection=local 2 | -------------------------------------------------------------------------------- /playbooks/bootstrap.yaml: -------------------------------------------------------------------------------- 1 | - hosts: bootstrap-node 2 | vars: 3 | bootstrapping: yes 4 | 5 | roles: 6 | - "{{ role|default('bootstrap') }}" 7 | -------------------------------------------------------------------------------- /playbooks/dep-graph.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Draw a dependency graph for all the roles.""" 4 | 5 | import sys 6 | import os 7 | import yaml 8 | 9 | here = os.path.abspath(os.path.dirname(__file__)) 10 | 11 | 12 | def main(): 13 | roles = os.listdir(os.path.join(here, 'roles')) 14 | print("digraph G {") 15 | 16 | for role in roles: 17 | meta_paths = [os.path.join(here, 'roles', role, 'meta', x) 18 | for x in ['main.yaml', 'main.yml']] 19 | for meta_path in meta_paths: 20 | if os.path.exists(meta_path): 21 | meta = yaml.safe_load(open(meta_path).read()) 22 | for dependency in meta.get('dependencies', []): 23 | dep = dependency if isinstance(dependency, str) else dependency['role'] 24 | print('"{}" -> "{}"'.format(role, dep)) 25 | break 26 | print("}") 27 | 28 | 29 | if __name__ == '__main__': 30 | sys.exit(main()) 31 | -------------------------------------------------------------------------------- /playbooks/dep-graph2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Draw a dependency graph for all the roles.""" 4 | 5 | import sys 6 | import os 7 | import yaml 8 | 9 | CWD = os.path.abspath(os.path.dirname(__file__)) 10 | ENTRY = 'bootstrap' 11 | SEEN = set() 12 | HIDE_SEEN = True 13 | 14 | 15 | def get_dep_roles(role_name): 16 | meta_paths = [ 17 | os.path.join(CWD, 'roles', role_name, 'meta', main_yaml) 18 | for main_yaml in ['main.yaml', 'main.yml'] 19 | ] 20 | meta_path = next((mp for mp in meta_paths if os.path.exists(mp)), None) 21 | if not meta_path: 22 | return None 23 | meta = yaml.safe_load(open(meta_path).read()) 24 | deps = meta.get('dependencies', []) 25 | dep_roles = [ 26 | d if isinstance(d, str) 27 | else d['role'] 28 | for d in deps 29 | ] 30 | return dep_roles 31 | 32 | 33 | def draw_dep_roles(dep_roles, level, hide_seen=HIDE_SEEN): 34 | for dep_role in dep_roles: 35 | if hide_seen and dep_role in SEEN: 36 | continue 37 | print('{}- {}'.format(' ' * (level + 1), dep_role)) 38 | SEEN.add(dep_role) 39 | next_dep_roles = get_dep_roles(dep_role) 40 | if not next_dep_roles: 41 | continue 42 | draw_dep_roles(next_dep_roles, level + 1) 43 | 44 | 45 | def main(entry=ENTRY): 46 | print(ENTRY) 47 | dep_roles = get_dep_roles(entry) 48 | draw_dep_roles(dep_roles, 0) 49 | 50 | 51 | if __name__ == '__main__': 52 | sys.exit(main()) 53 | -------------------------------------------------------------------------------- /playbooks/host_vars/bootstrap-node: -------------------------------------------------------------------------------- 1 | is_lain_manager: true 2 | is_swarm_manager: true 3 | is_deployd_node: true 4 | 5 | is_etcd_member: true 6 | is_consul_server: true 7 | etcd_members: 8 | - name: "{{ node_name }}" 9 | ip: "{{ node_ip }}" 10 | consul_servers: 11 | - "{{ node_ip }}" 12 | 13 | allow_restart_docker: yes 14 | -------------------------------------------------------------------------------- /playbooks/role.yaml: -------------------------------------------------------------------------------- 1 | - hosts: "{{ target|default('nodes') }}" 2 | roles: 3 | - "{{ role }}" 4 | -------------------------------------------------------------------------------- /playbooks/roles/ansible_plugins/files/plugins/callback/timestamp.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from ansible.plugins.callback import CallbackBase 4 | 5 | 6 | class CallbackModule(CallbackBase): 7 | """Show timestamp and time cost for each task run.""" 8 | 9 | def __init__(self): 10 | super(CallbackModule, self).__init__() 11 | 12 | self.timestamp = datetime.now() 13 | 14 | def playbook_on_task_start(self, name, is_conditional): 15 | now = datetime.now() 16 | self._display.display("{} (last task costs {})".format(now, now - self.timestamp)) 17 | self.timestamp = now 18 | -------------------------------------------------------------------------------- /playbooks/roles/ansible_plugins/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - copy: src=plugins/ dest=/usr/share/ansible/plugins/ 2 | -------------------------------------------------------------------------------- /playbooks/roles/backupd/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for backupd 2 | command: systemctl daemon-reload 3 | 4 | - name: restart backupd 5 | service: name=backupd state=restarted 6 | -------------------------------------------------------------------------------- /playbooks/roles/backupd/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: libraries 4 | - role: images 5 | images: 6 | - backupctl 7 | - role: console-deploy 8 | app: backupctl 9 | when: is_lain_manager 10 | -------------------------------------------------------------------------------- /playbooks/roles/backupd/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - fail: msg="moosefs not exist yet" 2 | when: mfsmaster=="" and backupd_driver=="moosefs" 3 | 4 | - name: check if backupd container exists 5 | command: docker inspect backupd-binary 6 | register: result 7 | ignore_errors: yes 8 | changed_when: False 9 | 10 | - name: create backupd binary container 11 | command: docker create --name backupd-binary {{ backupd_image }} /bin/bash 12 | when: result|failed 13 | 14 | - name: copy backupd binary to local /usr/bin 15 | command: docker cp backupd-binary:/usr/bin/backupd /tmp/backupd 16 | 17 | - name: remove the backupd-binary container 18 | command: docker rm -f backupd-binary 19 | 20 | - name: get stat of old backupd 21 | stat: path=/usr/bin/backupd 22 | register: ostat 23 | ignore_errors: yes 24 | 25 | - name: get stat of new backupd 26 | stat: path=/tmp/backupd 27 | register: nstat 28 | ignore_errors: yes 29 | 30 | - name: stop backupd service 31 | service: name=backupd state=stopped 32 | when: ostat.stat.exists 33 | 34 | - name: update backupd 35 | shell: cp /tmp/backupd /usr/bin/backupd && chmod +x /usr/bin/backupd 36 | register: binary_result 37 | when: ostat|failed or nstat|failed or "{{ostat.stat.md5|default('old')}}" != "{{nstat.stat.md5|default('new')}}" 38 | 39 | - name: install rsync 40 | package: name=rsync state=present 41 | 42 | - name: create backup directory 43 | file: path="{{ backup_dir }}/{{ node_ip }}" state=directory 44 | when: backupd_driver == "moosefs" and is_lain_manager 45 | 46 | - name: set the backup dir's trashtimeout on moosefs 47 | command: mfssettrashtime -r 3600 {{ backup_dir }} 48 | when: backupd_driver == "moosefs" and is_lain_manager 49 | 50 | - name: add backupd to service 51 | template: src=backupd.service.j2 dest=/etc/systemd/system/backupd.service 52 | register: service_result 53 | notify: 54 | - reload systemd for backupd 55 | 56 | - meta: flush_handlers 57 | 58 | - name: restart backupd 59 | service: name=backupd state=restarted 60 | when: (binary_result is defined and binary_result|success) or service_result|success 61 | 62 | - name: ensure backupd service started 63 | service: name=backupd enabled=yes state=started 64 | 65 | - name: config etcd to tag backup_enabled 66 | command: etcdctl set /lain/config/backup_enabled True 67 | when: is_lain_manager 68 | -------------------------------------------------------------------------------- /playbooks/roles/backupd/templates/backupd.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=backupd 3 | After=network.target 4 | 5 | [Service] 6 | User=root 7 | ExecStart=/usr/bin/backupd daemon \ 8 | -addr :{{ backupd_port }} \ 9 | -ip {{ node_ip }} \ 10 | -backup-driver {{ backupd_driver }} \ 11 | -backup-moosefs-dir {{ backup_dir }} 12 | Restart=always 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /playbooks/roles/binary/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: copy bootstrap binary to /tmp 2 | copy: src=lain dest=/tmp/ 3 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-binary-stop/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: get stat of old lainlet 2 | stat: path=/usr/bin/lainlet 3 | register: lainlet 4 | ignore_errors: yes 5 | 6 | - name: stop lainlet service 7 | service: name=lainlet state=stopped 8 | when: lainlet.stat.exists 9 | 10 | - name: get stat of old deployd 11 | stat: path=/usr/bin/deployd 12 | register: deployd 13 | ignore_errors: yes 14 | 15 | - name: stop deployd service 16 | service: name=deployd state=stopped 17 | when: deployd.stat.exists 18 | 19 | - name: test if networkd binary path exists 20 | stat: path=/usr/bin/networkd 21 | register: networkd 22 | ignore_errors: yes 23 | 24 | - name: stop networkd service 25 | service: name=networkd enabled=yes state=stopped 26 | when: networkd.stat.exists 27 | 28 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-console-deploy/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: bootstrap-console 3 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-console-deploy/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: check if app already deployed 2 | command: etcdctl ls /lain/deployd/pod_groups/{{ app }} 3 | register: result 4 | ignore_errors: yes 5 | changed_when: False 6 | 7 | - name: reposit app 8 | post_json: 9 | url: http://localhost:{{ bootstrap_console_port }}/api/v1/repos/ 10 | body: 11 | appname: "{{ app }}" 12 | when: result|failed 13 | 14 | - name: deploy app 15 | post_json: 16 | url: http://localhost:{{ bootstrap_console_port }}/api/v1/apps/ 17 | body: 18 | appname: "{{ app }}" 19 | when: result|failed 20 | 21 | - name: check app deployed 22 | shell: "curl -s console.lain/api/v1/apps/{{ app }}/ | python -c \"import json, sys; print json.load(sys.stdin)['app']['procs'][0]['pods'][0]['containerid']\"" 23 | register: container_id 24 | until: container_id|success 25 | retries: 50 26 | delay: 5 27 | changed_when: False 28 | 29 | - name: remove bootstrap-webrouter nginx config 30 | file: path={{ bootstrap_volumes_dir }}/webrouter/nginx/etc/nginx/bootstrap/{{ app }}.lain.conf state=absent 31 | register: result 32 | 33 | - name: reload bootstrap-webrouter 34 | command: docker exec bootstrap-webrouter nginx -s reload 35 | when: result|changed 36 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-console-start/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: bootstrap-console 3 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-console-start/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: prepare volume dirs 2 | file: path={{ bootstrap_volumes_dir }}/console/{{ item }} state=directory 3 | with_items: 4 | - web/externalbin 5 | - web/lain/app/logs 6 | 7 | - name: copy volume files 8 | copy: src={{ item.src }} dest={{ bootstrap_volumes_dir }}/console/{{ item.dest }} mode={{ item.mode }} 9 | with_items: 10 | - src: /usr/bin/calicoctl 11 | dest: web/externalbin/calicoctl 12 | mode: "0755" 13 | 14 | - name: inspect bootstrap-console 15 | docker_inspect: name=bootstrap-console 16 | register: inspect_console 17 | ignore_errors: yes 18 | 19 | - name: remove deprected bootstrap-console 20 | command: docker rm -f bootstrap-console 21 | when: inspect_console|success and not inspect_console.State.Running 22 | 23 | - name: run bootstrap-console 24 | command: > 25 | docker run -d 26 | --name bootstrap-console 27 | -p {{ bootstrap_console_port }}:8000 28 | -v {{ bootstrap_volumes_dir }}/console/web/externalbin:/externalbin 29 | -v {{ bootstrap_volumes_dir }}/console/web/lain/app/logs:/lain/app/logs 30 | {{ bootstrap_console_image }} 31 | ./entry.sh 32 | when: inspect_console|failed or not inspect_console.State.Running 33 | 34 | - name: wait for console ready 35 | wait_for: host=localhost port={{ bootstrap_console_port }} timeout=10 36 | 37 | # vim: set filetype=ansible.yaml: 38 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-console-stop/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: bootstrap-console 3 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-console-stop/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: stop bootstrap-console 2 | docker: 3 | image: "{{ bootstrap_console_image }}" 4 | name: bootstrap-console 5 | state: absent 6 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-console/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: libraries 4 | - role: docker 5 | - role: packages 6 | - role: etcd 7 | - role: bootstrap-registry 8 | action: start 9 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-docker/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: bootstrap-images 4 | - role: libraries 5 | - role: docker 6 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-docker/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: check if all images exist 2 | docker_check_images: 3 | images: "{{ bootstrap_images.values() }}" 4 | register: check 5 | ignore_errors: yes 6 | changed_when: False 7 | 8 | - name: load saved images (this may take minutes...) 9 | shell: xz -d -c {{ saved_images }} | docker load 10 | when: check|failed and saved_images is defined 11 | register: load 12 | 13 | - name: check if all images exist, again 14 | docker_check_images: 15 | images: "{{ bootstrap_images.values() }}" 16 | register: check 17 | ignore_errors: yes 18 | changed_when: False 19 | 20 | - name: pull images if not exists (use --saved-images option if your connection to DockerHub is very slow) 21 | docker_pull_image: image={{ item }} registry={{ registry_bootstrap|default('') }} 22 | with_items: "{{ bootstrap_images.values() }}" 23 | when: check|failed 24 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-etcd/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: bootstrap-binary-stop 3 | - role: etcd 4 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-etcd/templates/etcd.cron.j2: -------------------------------------------------------------------------------- 1 | 1 7,12,20,23 * * * root /bin/systemctl restart etcd 2>&1 > /dev/null -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-firewall/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: firewall 4 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-firewall/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: clean lain-PREROUTING 2 | shell: "iptables -t nat -F lain-PREROUTING" 3 | 4 | - name: clean lain-OUTPUT 5 | shell: "iptables -t nat -F lain-OUTPUT" 6 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-images/vars/main.yaml: -------------------------------------------------------------------------------- 1 | # vim: set filetype=json: 2 | # This file is also used by bootstrap python script. Please keep JSON format for this file. 3 | { 4 | "bootstrap_images": { 5 | "swarm": "swarm:1.2.8", 6 | "rebellion": "rebellion:v2.3.3", 7 | "mysql": "mysql-server:5.6.30", 8 | "registry": "registry:release-1498029368-2c74d73cef3f020dd1dd6fcf9e1933112a2c67cc", 9 | "registry-meta": "registry:meta-1498029368-2c74d73cef3f020dd1dd6fcf9e1933112a2c67cc", 10 | "tinydns": "tinydns:release-1497423817-5c5982f1e53476478b74a28877bd878a30605b49", 11 | "tinydns-meta": "tinydns:meta-1497423817-5c5982f1e53476478b74a28877bd878a30605b49", 12 | "webrouter": "webrouter:release-1497430313-fa743a7d70301660065d6e636a18dab5fda3173c", 13 | "webrouter-meta": "webrouter:meta-1497430313-fa743a7d70301660065d6e636a18dab5fda3173c", 14 | "console": "console:release-1500017557-5eb4e8b77fa3a7a5545150b4309a37bee3158c33", 15 | "console-meta": "console:meta-1500017557-5eb4e8b77fa3a7a5545150b4309a37bee3158c33", 16 | "backupctl-meta": "backupctl:meta-1498032948-eb5dc315425981a4ab59e7515627b8b34ef8ed5c", 17 | "backupctl": "backupctl:release-1498032948-eb5dc315425981a4ab59e7515627b8b34ef8ed5c", 18 | "lvault-meta": "lvault:meta-1485154203-73072c92b1862ab470b8d872b278f734747b0739", 19 | "lvault": "lvault:release-1485154203-73072c92b1862ab470b8d872b278f734747b0739" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-layer0/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: bootstrap-firewall 3 | 4 | # store cluster configs in etcd 5 | - role: bootstrap-etcd 6 | 7 | - role: consul 8 | 9 | # load all required images 10 | - role: bootstrap-docker 11 | 12 | - role: prepare 13 | 14 | - role: lainlet 15 | 16 | - role: networkd 17 | 18 | - role: calico 19 | 20 | - role: swarm 21 | 22 | - role: deployd 23 | 24 | - role: binary 25 | 26 | - role: rsync 27 | 28 | - role: mysql 29 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-layer1/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | 3 | # start layer1 apps using docker directly 4 | 5 | - role: bootstrap-registry-start 6 | 7 | - role: bootstrap-webrouter-start 8 | 9 | - role: bootstrap-console-start 10 | 11 | # deploy registry using bootstrap-console 12 | 13 | - role: bootstrap-registry-push 14 | images_to_push: 15 | - registry 16 | - registry-meta 17 | 18 | - role: bootstrap-console-deploy 19 | app: registry 20 | 21 | # we have registry.lain.local now, no need for bootstrap-registry 22 | 23 | - role: bootstrap-registry-stop 24 | 25 | # prepare to deploy other layer1 apps 26 | 27 | - role: registry 28 | action: push 29 | images_to_push: "{{ bootstrap_images.keys() }}" 30 | 31 | # deploy dns app 32 | - role: bootstrap-tinydns 33 | 34 | # deploy console app 35 | - role: console 36 | - role: bootstrap-console-deploy 37 | app: console 38 | 39 | - role: bootstrap-console-stop 40 | 41 | # deploy webrouter 42 | - role: webrouter-start 43 | 44 | - role: bootstrap-webrouter-stop 45 | 46 | - role: lvault-app 47 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-registry-push/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: bootstrap-registry 3 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-registry-push/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: wait_for registry ready 2 | command: curl -m 2 http://registry.lain.local/v2/ 3 | register: result 4 | until: "result.stdout.startswith('{')" 5 | retries: 50 6 | delay: 5 7 | changed_when: False 8 | 9 | - name: push image to registry (may take minutes) 10 | docker_push: 11 | image: "{{ bootstrap_images[item] }}" 12 | registry: registry.lain.local 13 | with_items: "{{images_to_push|default([])}}" 14 | 15 | # vim: set filetype=ansible.yaml: 16 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-registry-start/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: bootstrap-registry 3 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-registry-start/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: check if registry app is deployed 2 | command: etcdctl ls /lain/deployd/pod_groups/registry 3 | register: result 4 | ignore_errors: yes 5 | changed_when: False 6 | 7 | - include: start.yaml 8 | when: result|failed 9 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-registry-start/tasks/start.yaml: -------------------------------------------------------------------------------- 1 | - name: inspect existing bootstrap registry 2 | docker_inspect: name=bootstrap-registry type=container 3 | register: inspect 4 | ignore_errors: yes 5 | 6 | - name: run bootstrap-registry 7 | command: > 8 | docker run -d 9 | --name bootstrap-registry 10 | -p {{ bootstrap_registry_port }}:5000 11 | {{ bootstrap_registry_image }} 12 | ./entry.sh 13 | when: inspect|failed or not inspect.State.Running 14 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-registry-stop/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: bootstrap-registry 3 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-registry-stop/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: stop bootstrap-registry 2 | docker: 3 | image: "{{ bootstrap_registry_image }}" 4 | name: bootstrap-registry 5 | state: absent 6 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-registry/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: libraries 4 | - role: packages 5 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-tinydns/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: binary 3 | - role: firewall 4 | - role: bootstrap-console-deploy 5 | app: tinydns 6 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-tinydns/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: set tinydns vip port config 2 | set_virtual_ip_key: ip="0.0.0.0" port="{{ dns_port }}" container_app="tinydns" container_proc="worker" container_proto="udp" container_port="53" 3 | 4 | - name: set tinydns domain lain 5 | set_tinydns_domain: domain="lain" record=".lain:{{ node_ip }}:a:300" 6 | 7 | - name: set tinydns domain .20.172.in-addr.arpa 8 | set_tinydns_domain: domain=".20.172.in-addr.arpa" record=".20.172.in-addr.arpa:{{ node_ip }}:a:300" 9 | 10 | - name: set tinydns domain DOMAIN 11 | set_tinydns_domain: domain="{{ domain }}" record=".{{ domain }}:{{ node_ip }}:a:300" 12 | when: domain == 'lain.local' 13 | 14 | - name: set tinydns wildcard domain lain 15 | set_tinydns_domain: domain="webrouter.lain" record="+webrouter.lain:{{ vip }}:300" 16 | when: vip != '0.0.0.0' 17 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-webrouter-start/files/nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | server_name _; 4 | 5 | #charset koi8-r; 6 | access_log /var/log/nginx/default.access.log main; 7 | 8 | location / { 9 | root /usr/share/nginx/html; 10 | index index.html index.htm; 11 | } 12 | 13 | #error_page 404 /404.html; 14 | 15 | # redirect server error pages to the static page /50x.html 16 | # 17 | error_page 500 502 503 504 /50x.html; 18 | location = /50x.html { 19 | root /usr/share/nginx/html; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-webrouter-start/files/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | user nginx; 3 | worker_processes 1; 4 | 5 | error_log /var/log/nginx/error.log warn; 6 | pid /var/run/nginx.pid; 7 | 8 | 9 | events { 10 | worker_connections 1024; 11 | } 12 | 13 | 14 | http { 15 | include /etc/nginx/mime.types; 16 | default_type application/octet-stream; 17 | server_names_hash_bucket_size 512; 18 | 19 | log_format main '$remote_addr - $remote_user [$time_local] "$Host" "$request" ' 20 | '$status $body_bytes_sent "$http_referer" ' 21 | '"$http_user_agent" "$http_x_forwarded_for" ' 22 | 'upstream_response_time "$upstream_response_time" request_time "$request_time"'; 23 | client_max_body_size 0; 24 | chunked_transfer_encoding on; 25 | 26 | access_log /var/log/nginx/access.log main; 27 | 28 | sendfile on; 29 | #tcp_nopush on; 30 | 31 | keepalive_timeout 65; 32 | 33 | #gzip on; 34 | 35 | include /etc/nginx/upstreams/*.upstreams; 36 | include /etc/nginx/conf.d/*.conf; 37 | include /etc/nginx/bootstrap/*.conf; 38 | include /etc/nginx/default.conf; 39 | } 40 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-webrouter-start/files/nginx/proxy.conf: -------------------------------------------------------------------------------- 1 | proxy_next_upstream error timeout invalid_header http_500 http_502 http_504; 2 | 3 | proxy_redirect off; 4 | proxy_set_header Host $host; 5 | proxy_set_header X-Real-IP $remote_addr; 6 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 7 | proxy_set_header X-Original-URI $request_uri; 8 | proxy_set_header REQUEST_URI $request_uri; 9 | 10 | set $xproto $scheme; 11 | if ($http_x_forwarded_proto ~* "^http") { 12 | set $xproto $http_x_forwarded_proto; 13 | } 14 | proxy_set_header X-Forwarded-Proto $xproto; 15 | 16 | #client_max_body_size 10m; 17 | client_body_buffer_size 128k; 18 | proxy_connect_timeout 90; 19 | proxy_send_timeout 90; 20 | proxy_read_timeout 900; 21 | proxy_buffer_size 4k; 22 | proxy_buffers 4 32k; 23 | proxy_busy_buffers_size 64k; 24 | proxy_temp_file_write_size 64k; 25 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-webrouter-start/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: bootstrap-webrouter 4 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-webrouter-start/templates/nginx/console.conf.j2: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen 443 ssl; 4 | server_name .console.{{ domain }} .console.lain; 5 | ssl_certificate /etc/nginx/ssl/web.crt; 6 | ssl_certificate_key /etc/nginx/ssl/web.key; 7 | include proxy.conf; 8 | 9 | location / { 10 | 11 | proxy_pass http://{{ node_ip }}:{{ bootstrap_console_port }}; 12 | 13 | } 14 | access_log /var/log/nginx/web.console.access.log main; 15 | } 16 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-webrouter-start/templates/nginx/registry.conf.j2: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen 443 ssl; 4 | server_name .registry.{{ domain }}; 5 | ssl_certificate /etc/nginx/ssl/web.crt; 6 | ssl_certificate_key /etc/nginx/ssl/web.key; 7 | include proxy.conf; 8 | 9 | location / { 10 | 11 | proxy_pass http://{{ node_ip }}:{{ bootstrap_registry_port }}; 12 | 13 | } 14 | access_log /var/log/nginx/web.registry.access.log main; 15 | } 16 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-webrouter-stop/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: bootstrap-webrouter 3 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-webrouter-stop/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: stop bootstrap-webrouter 2 | docker: 3 | image: "{{ bootstrap_webrouter_image }}" 4 | name: bootstrap-webrouter 5 | state: absent 6 | 7 | - name: remove console.lain 8 | set_config_domain: domain="console.lain" record="" 9 | 10 | - name: remove console.DOMAIN 11 | set_config_domain: domain="console.{{ domain }}" record="" 12 | 13 | - name: remove registry.DOMAIN 14 | set_config_domain: domain="registry.{{ domain }}" record="" 15 | 16 | - name: wait for DNS resolution ready 17 | pause: seconds=2 18 | 19 | - name: check new webrouter works well 20 | shell: "curl -s registry.lain.local/v2/" 21 | register: result 22 | until: result|success 23 | retries: 50 24 | delay: 5 25 | changed_when: False 26 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap-webrouter/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: libraries 4 | - role: bootstrap-registry 5 | - role: bootstrap-console 6 | - role: etcd 7 | - role: ssl 8 | - role: packages 9 | - role: networkd 10 | -------------------------------------------------------------------------------- /playbooks/roles/bootstrap/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: libraries 4 | - role: systemd 5 | - role: bootstrap-layer0 6 | - role: bootstrap-layer1 7 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico.conf: -------------------------------------------------------------------------------- 1 | xt_set 2 | ip6_tables 3 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/conf.d/bird.toml.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | src = "bird.toml.template" 3 | dest = "/etc/calico/confd/conf.d/bird.toml" 4 | prefix = "/calico/bgp/v1/global" 5 | keys = [ 6 | "/node_mesh", 7 | ] 8 | reload_cmd = "systemctl restart calico-confd" 9 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/conf.d/bird6.toml.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | src = "bird6.toml.template" 3 | dest = "/etc/calico/confd/conf.d/bird6.toml" 4 | prefix = "/calico/bgp/v1/global" 5 | keys = [ 6 | "/node_mesh" 7 | ] 8 | reload_cmd = "systemctl restart calico-confd" 9 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/conf.d/bird6_ipam.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | src = "bird6_ipam.cfg.template" 3 | dest = "/etc/calico/confd/config/bird6_ipam.cfg" 4 | prefix = "/calico/v1/ipam/v6" 5 | keys = [ 6 | "/pool", 7 | ] 8 | reload_cmd = "pkill -HUP bird6 || true" 9 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/conf.d/custom_filters.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | src = "custom_filters.cfg.template" 3 | dest = "/etc/calico/confd/config/custom_filters.cfg" 4 | prefix = "/calico/bgp/v1/global/custom_filters" 5 | keys = [ 6 | "/v4", 7 | ] 8 | reload_cmd = "pkill -HUP bird || true" 9 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/conf.d/custom_filters6.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | src = "custom_filters6.cfg.template" 3 | dest = "/etc/calico/confd/config/custom_filters6.cfg" 4 | prefix = "/calico/bgp/v1/global/custom_filters" 5 | keys = [ 6 | "/v6", 7 | ] 8 | reload_cmd = "pkill -HUP bird || true" 9 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/conf.d/tunl-ip.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | src = "tunl-ip.template" 3 | dest = "/tmp/tunl-ip" 4 | prefix = "/calico/v1/ipam/v4" 5 | keys = [ 6 | "/pool", 7 | ] 8 | reload_cmd = "allocate-ipip-addr" 9 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laincloud/lain/0fe5f93d06540300c5dafb94d81b3b69d2c8f1da/playbooks/roles/calico/files/calico/confd/config/.gitkeep -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/templates/bird6_ipam.cfg.template: -------------------------------------------------------------------------------- 1 | # Generated by confd 2 | filter calico_pools { 3 | calico_aggr(); 4 | custom_filters(); 5 | {{range ls "/pool"}}{{$data := json (getv (printf "/pool/%s" .))}} 6 | if ( net ~ {{$data.cidr}} ) then { 7 | accept; 8 | } 9 | {{end}} 10 | reject; 11 | } 12 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/templates/bird_aggr.cfg.template: -------------------------------------------------------------------------------- 1 | # Generated by confd 2 | # ------------- Static black hole addresses ------------- 3 | {{if ls "/"}} 4 | protocol static { 5 | {{range ls "/"}} 6 | {{$parts := split . "-"}} 7 | {{$cidr := join $parts "/"}} 8 | route {{$cidr}} blackhole; 9 | {{end}} 10 | } 11 | {{else}}# No static routes configured.{{end}} 12 | 13 | # Aggregation of routes on this host; export the block, nothing beneath it. 14 | function calico_aggr () 15 | { 16 | {{range ls "/"}} 17 | {{$parts := split . "-"}} 18 | {{$cidr := join $parts "/"}} 19 | if ( net = {{$cidr}} ) then { accept; } 20 | if ( net ~ {{$cidr}} ) then { reject; } 21 | {{end}} 22 | } 23 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/templates/custom_filters.cfg.template: -------------------------------------------------------------------------------- 1 | # Generated by confd 2 | function custom_filters () 3 | { 4 | {{range ls "/v4"}}{{$data := getv (printf "/v4/%s" .)}} 5 | {{ $data }} 6 | {{end}} 7 | } 8 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/templates/custom_filters6.cfg.template: -------------------------------------------------------------------------------- 1 | # Generated by confd 2 | function custom_filters () 3 | { 4 | {{range ls "/v6"}}{{$data := getv (printf "/v6/%s" .)}} 5 | {{ $data }} 6 | {{end}} 7 | } 8 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/calico/confd/templates/tunl-ip.template: -------------------------------------------------------------------------------- 1 | We must dump all pool data to this file to trigger a resync. 2 | Otherwise, confd notices the file hasn't changed and won't 3 | run our python update script. 4 | 5 | {{range ls "/pool"}}{{$data := json (getv (printf "/pool/%s" .))}} 6 | {{if $data.ipip}}{{if not $data.disabled}}{{$data.cidr}}{{end}}{{end}} 7 | {{end}} 8 | -------------------------------------------------------------------------------- /playbooks/roles/calico/files/service/calico-bird.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Calico bird 3 | Requires=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | Restart=on-failure 8 | ExecStartPre=/usr/bin/env mkdir -p /var/run/calico 9 | ExecStart=/usr/bin/bird -R -s /var/run/calico/bird.ctl -d -c /etc/calico/confd/config/bird.cfg 10 | 11 | [Install] 12 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /playbooks/roles/calico/files/service/calico-bird6.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Calico bird6 3 | Requires=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | Restart=on-failure 8 | ExecStartPre=/usr/bin/env mkdir -p /var/run/calico 9 | ExecStart=/usr/bin/bird6 -R -s /var/run/calico/bird6.ctl -d -c /etc/calico/confd/config/bird6.cfg 10 | 11 | [Install] 12 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /playbooks/roles/calico/files/service/calico-felix.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Calico Felix agent 3 | After=syslog.target network.target 4 | 5 | [Service] 6 | User=root 7 | ExecStartPre=/usr/bin/env mkdir -p /var/run/calico 8 | ExecStart=/usr/bin/calico-felix 9 | KillMode=process 10 | Restart=on-failure 11 | LimitNOFILE=32000 12 | 13 | [Install] 14 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /playbooks/roles/calico/files/service/calico-libnetwork.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Calico libnetwork plugin 3 | Requires=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | EnvironmentFile=/etc/calico/calico.env 8 | Restart=on-failure 9 | ExecStart=/usr/bin/libnetwork-plugin 10 | 11 | [Install] 12 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /playbooks/roles/calico/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: os-dependent-vars 3 | - role: config 4 | - role: firewall 5 | - role: etcd 6 | -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/bird.toml.template.j2: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | [template] 3 | src = "bird.cfg.{{if (json (getv "/node_mesh")).enabled}}mesh{{else}}no-mesh{{end}}.template" 4 | dest = "/etc/calico/confd/config/bird.cfg" 5 | prefix = "/calico/bgp/v1" 6 | keys = [ 7 | {% endraw %} 8 | {{ '{{if (json (getv "/node_mesh")).enabled}}"/host"{{else}}"/host/' }}{{ ansible_nodename }}{{ '"{{end}},' }} 9 | {% raw %} 10 | "/global" 11 | ] 12 | check_cmd = "bird -p -c {{"{{"}}.src{{"}}"}}" 13 | reload_cmd = "pkill -HUP bird || true" 14 | {% endraw %} -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/bird6.toml.template.j2: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | [template] 3 | src = "bird6.cfg.{{if (json (getv "/node_mesh")).enabled}}mesh{{else}}no-mesh{{end}}.template" 4 | dest = "/etc/calico/confd/config/bird6.cfg" 5 | prefix = "/calico/bgp/v1" 6 | keys = [ 7 | {% endraw %} 8 | {{ '{{if (json (getv "/node_mesh")).enabled}}"/host"{{else}}"/host/' }}{{ ansible_nodename }}{{ '"{{end}},' }} 9 | {% raw %} 10 | "/global" 11 | ] 12 | check_cmd = "bird6 -p -c {{"{{"}}.src{{"}}"}}" 13 | reload_cmd = "pkill -HUP bird6 || true" 14 | {% endraw %} 15 | -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/bird6_aggr.toml.j2: -------------------------------------------------------------------------------- 1 | [template] 2 | src = "bird_aggr.cfg.template" 3 | dest = "/etc/calico/confd/config/bird6_aggr.cfg" 4 | prefix = "/calico/ipam/v2/host/{{ ansible_nodename }}/ipv6/block" 5 | keys = [ 6 | "/", 7 | ] 8 | reload_cmd = "pkill -HUP bird || true" 9 | -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/bird_aggr.toml.j2: -------------------------------------------------------------------------------- 1 | [template] 2 | src = "bird_aggr.cfg.template" 3 | dest = "/etc/calico/confd/config/bird_aggr.cfg" 4 | prefix = "/calico/ipam/v2/host/{{ ansible_nodename }}/ipv4/block" 5 | keys = [ 6 | "/", 7 | ] 8 | reload_cmd = "pkill -HUP bird || true" 9 | -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/bird_ipam.cfg.template.j2: -------------------------------------------------------------------------------- 1 | {% raw %} 2 | #Generated by confd 3 | filter calico_pools { 4 | calico_aggr(); 5 | custom_filters(); 6 | {{range ls "/v1/ipam/v4/pool"}}{{$data := json (getv (printf "/v1/ipam/v4/pool/%s" .))}} 7 | if ( net ~ {{$data.cidr}} ) then { 8 | accept; 9 | } 10 | {{end}} 11 | reject; 12 | } 13 | 14 | {% endraw %} 15 | {{ '{{$network_key := "/bgp/v1/host/' }}{{ ansible_nodename }}{{ '/network_v4"}}{{$network := getv $network_key}}' }} 16 | {% raw %} 17 | filter calico_ipip { 18 | {{range ls "/v1/ipam/v4/pool"}}{{$data := json (getv (printf "/v1/ipam/v4/pool/%s" .))}} 19 | if ( net ~ {{$data.cidr}} ) then { 20 | {{if $data.ipip_mode}}{{if eq $data.ipip_mode "cross-subnet"}} 21 | if ( from ~ {{$network}} ) then 22 | krt_tunnel = ""; {{/* Destination in ipPool, mode is cross sub-net, route from-host on subnet, do not use IPIP */}} 23 | else 24 | krt_tunnel = "{{$data.ipip}}"; {{/* Destination in ipPool, mode is cross sub-net, route from-host off subnet, set the tunnel (if IPIP not enabled, value will be "") */}} 25 | accept; 26 | } {{else}} 27 | krt_tunnel = "{{$data.ipip}}"; {{/* Destination in ipPool, mode not cross sub-net, set the tunnel (if IPIP not enabled, value will be "") */}} 28 | accept; 29 | } {{end}} {{else}} 30 | krt_tunnel = "{{$data.ipip}}"; {{/* Destination in ipPool, mode field is not present, set the tunnel (if IPIP not enabled, value will be "") */}} 31 | accept; 32 | } {{end}} 33 | {{end}} 34 | accept; {{/* Destination is not in any ipPool, accept */}} 35 | } 36 | {% endraw %} -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/bird_ipam.toml.j2: -------------------------------------------------------------------------------- 1 | [template] 2 | src = "bird_ipam.cfg.template" 3 | dest = "/etc/calico/confd/config/bird_ipam.cfg" 4 | prefix = "/calico" 5 | keys = [ 6 | "/v1/ipam/v4/pool", 7 | "/bgp/v1/host/{{ ansible_nodename }}" 8 | ] 9 | reload_cmd = "pkill -HUP bird || true" 10 | -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/calico-confd.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Calico confd 3 | Requires=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | Restart=on-failure 8 | ExecStart=/usr/bin/confd -confdir=/etc/calico/confd -interval=5 -watch -no-discover --log-level=debug -node=http://127.0.0.1:{{ etcd_client_port }} 9 | 10 | [Install] 11 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/calico.env.j2: -------------------------------------------------------------------------------- 1 | ETCD_AUTHORITY=127.0.0.1:{{ etcd_client_port }} 2 | DOCKER_API_VERSION=1.24 -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/calicoctl.cfg.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: calicoApiConfig 3 | metadata: {} 4 | spec: 5 | datastoreType: etcdv2 6 | etcdAuthority: "" 7 | etcdCACertFile: "" 8 | etcdCertFile: "" 9 | etcdEndpoints: http://127.0.0.1:{{ etcd_client_port }} 10 | etcdKeyFile: "" 11 | etcdPassword: "" 12 | etcdScheme: "" 13 | etcdUsername: "" 14 | k8sAPIEndpoint: "" 15 | k8sAPIToken: "" 16 | k8sCAFile: "" 17 | k8sCertFile: "" 18 | k8sDisableNodePoll: false 19 | k8sInsecureSkipTLSVerify: false 20 | k8sKeyFile: "" 21 | kubeconfig: "" -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/felix.cfg.j2: -------------------------------------------------------------------------------- 1 | [global] 2 | FelixHostname = "{{ ansible_nodename }}" 3 | EtcdEndpoints = "http://127.0.0.1:{{ etcd_client_port }}" -------------------------------------------------------------------------------- /playbooks/roles/calico/templates/ippool.yml.j2: -------------------------------------------------------------------------------- 1 | - apiVersion: v1 2 | kind: ipPool 3 | metadata: 4 | cidr: {{ calico_default_network }} 5 | spec: 6 | {% if calico_ipip|bool %} 7 | ipip: 8 | enabled: true 9 | mode: always 10 | {% endif %} 11 | nat-outgoing: true -------------------------------------------------------------------------------- /playbooks/roles/ceph-fuse-new/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config -------------------------------------------------------------------------------- /playbooks/roles/ceph-fuse-new/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: install ceph-fuse 2 | yum: 3 | name: ceph-fuse-{{ ceph_version }} 4 | state: present 5 | 6 | - name: create ceph conf dir 7 | file: path=/etc/ceph state=directory 8 | 9 | - name: create ceph client_mountpoint dir 10 | file: path={{client_mountpoint}} state=directory 11 | 12 | - name: copy keyring 13 | template: src=ceph.client.lain.keyring.j2 dest=/etc/ceph/ceph.{{ceph_account}}.keyring 14 | 15 | - name: chmod 0600 keyring 16 | file: path=/etc/ceph/ceph.{{ceph_account}}.keyring mode=0600 17 | 18 | - name: copy ceph.conf 19 | template: src=ceph.conf.j2 dest=/etc/ceph/ceph.conf 20 | 21 | - name: mount ceph 22 | command: ceph-fuse -n client.lain -r /lain /data/lain/cloud-volumes/ 23 | ignore_errors: yes 24 | 25 | - name: chmod 755 rc.local 26 | file: path=/etc/rc.d/rc.local mode=0755 27 | 28 | - name: edit rc.local 29 | lineinfile: dest=/etc/rc.local line="ceph-fuse -n {{ceph_account}} -r {{ceph_mountpoint}} {{client_mountpoint}}" insertafter=EOF 30 | -------------------------------------------------------------------------------- /playbooks/roles/ceph-fuse-new/templates/ceph.client.lain.keyring.j2: -------------------------------------------------------------------------------- 1 | [{{ceph_account}}] 2 | key = {{ceph_password}} 3 | -------------------------------------------------------------------------------- /playbooks/roles/ceph-fuse-new/templates/ceph.conf.j2: -------------------------------------------------------------------------------- 1 | [global] 2 | mon_host = {{ceph_mon_address}} 3 | -------------------------------------------------------------------------------- /playbooks/roles/ceph-fuse/files/plugins/ceph_mon.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import argparse 3 | import time 4 | import gevent 5 | import logging 6 | import sys 7 | from gevent import monkey 8 | monkey.patch_socket() 9 | 10 | CEPH_FUSE_MOUNT_TYPE = 'ceph-fuse' 11 | CHECK_INTERVAL_SEC = 60 12 | 13 | log = logging.getLogger("cephmon") 14 | log.setLevel(logging.INFO) 15 | 16 | ch = logging.StreamHandler(sys.stdout) 17 | formatter = logging.Formatter( 18 | '%(asctime)s - %(name)s - %(levelname)s - %(message)s') 19 | ch.setFormatter(formatter) 20 | log.addHandler(ch) 21 | 22 | 23 | def check_mountpoint(mount_point): 24 | if mount_point.strip(' ') == '': 25 | return 26 | check_cmd = ['df', '-P', mount_point] 27 | check_pipe = subprocess.Popen(check_cmd, stdout=subprocess.PIPE) 28 | filter_pipe = subprocess.Popen( 29 | ["tail", "-n", "+2"], stdin=check_pipe.stdout, stdout=subprocess.PIPE) 30 | result_pipe = subprocess.Popen( 31 | ["awk", "{print $1}"], stdin=filter_pipe.stdout, stdout=subprocess.PIPE) 32 | filter_pipe.stdout.close() 33 | check_pipe.stdout.close() 34 | result, err = result_pipe.communicate() 35 | return result.strip() 36 | 37 | 38 | def monitor(mount_point): 39 | while True: 40 | if check_mountpoint(mount_point) == CEPH_FUSE_MOUNT_TYPE: 41 | log.debug(mount_point + ' Successed') 42 | else: 43 | log.error(mount_point + ' Failed') 44 | remount(mount_point) 45 | time.sleep(CHECK_INTERVAL_SEC) 46 | 47 | 48 | def start_monitors(mount_points): 49 | greenlets = [] 50 | for mp in mount_points: 51 | greenlet = gevent.spawn(monitor, mp) 52 | greenlet.start() 53 | greenlets.append(greenlet) 54 | gevent.joinall(greenlets) 55 | 56 | 57 | def fetch_mount_points(conf): 58 | with open(conf) as mps_file: 59 | lines = mps_file.readlines() 60 | mps = [] 61 | for line in lines: 62 | mps.append(line.strip()) 63 | return mps 64 | 65 | def remount(mount_point): 66 | umount_cmd = ['umount', mount_point] 67 | try: 68 | subprocess.check_output(umount_cmd) 69 | except Exception as e: 70 | log.error(e) 71 | log.error('unmount '+ mount_point + ' Failed') 72 | mount_cmd = ['mount', mount_point] 73 | try: 74 | subprocess.check_output(mount_cmd) 75 | log.info('remount '+ mount_point + ' Successed') 76 | except Exception as e: 77 | log.error(e) 78 | log.error('mount '+ mount_point + ' Failed') 79 | 80 | if __name__ == "__main__": 81 | parser = argparse.ArgumentParser() 82 | parser.add_argument("--conf", help="Ceph fuse mountpoint", 83 | default="/etc/ceph/fuse.conf", type=str) 84 | args = parser.parse_args() 85 | mount_points = fetch_mount_points(args.conf) 86 | start_monitors(mount_points) 87 | -------------------------------------------------------------------------------- /playbooks/roles/ceph-fuse/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config -------------------------------------------------------------------------------- /playbooks/roles/ceph-fuse/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: install ceph-fuse 2 | yum: 3 | name: ceph-fuse-{{ ceph_version }} 4 | state: present 5 | 6 | # 在centos7里, /etc/fstab 不支持 '=', 所以我们用 ':' 替代 7 | # 但是在用 ':' 替代之后需要修改ceph-fuse mount的代码,使其先将 ':' 替换成 '=' 8 | 9 | - name: backup /sbin/mount.fuse.ceph 10 | command: mv /sbin/mount.fuse.ceph /sbin/mount.fuse.ceph.bak 11 | when: ansible_distribution_major_version == "7" 12 | 13 | - name: update ceph mount file, 14 | shell: sed -e "s/cephargs='--'\`echo \$1 | sed 's\/,\/ --\/g'\`/fs_spec=\`echo \$1 | sed \'s\/:\/=\/g\'\` \ncephargs=\'--\'\`echo \$fs_spec | sed \'s\/,\/ --\/g\'\`/g" /sbin/mount.fuse.ceph.bak >/sbin/mount.fuse.ceph 15 | when: ansible_distribution_major_version == "7" 16 | 17 | - name: chmod mount.fuse.ceph 18 | file: path=/sbin/mount.fuse.ceph state=file mode=755 group=root owner=root 19 | 20 | - name: create ceph conf dir 21 | command: mkdir -p /etc/ceph 22 | ignore_errors: yes 23 | 24 | - name: create ceph client_mountpoint dir 25 | command: mkdir -p {{client_mountpoint}} 26 | ignore_errors: yes 27 | 28 | - name: copy keyring 29 | template: src=ceph.client.lain.keyring.j2 dest=/etc/ceph/ceph.client.{{ceph_account}}.keyring 30 | 31 | - name: copy ceph.conf 32 | template: src=ceph.conf.j2 dest=/etc/ceph/ceph.conf 33 | 34 | - name: edit fstab 35 | lineinfile: dest=/etc/fstab line="id:{{ceph_account}},client_mountpoint:{{ceph_mountpoint}} {{client_mountpoint}} fuse.ceph _netdev 0 0" insertafter=EOF 36 | 37 | - name: mount 38 | command: mount -a 39 | 40 | - name: init fuse.conf if not exist 41 | command: touch /etc/ceph/fuse.conf 42 | 43 | - name: edit fuse.conf 44 | lineinfile: dest=/etc/ceph/fuse.conf line="{{client_mountpoint}}" insertafter=EOF 45 | 46 | - name: install gevent 47 | package: name=python-gevent state=present 48 | 49 | - name: copy cephmon 50 | copy: src=plugins dest=/etc/ceph/ mode=0755 51 | 52 | - name: generate cephmon service 53 | template: src=cephmon.service.j2 dest=/etc/systemd/system/cephmon.service 54 | register: result 55 | 56 | - name: reload systemd 57 | command: systemctl daemon-reload 58 | when: result|changed 59 | 60 | - name: "enable cephmon" 61 | service: name=cephmon enabled=yes 62 | 63 | - name: restart cephmon 64 | service: name=cephmon state=restarted 65 | when: result 66 | -------------------------------------------------------------------------------- /playbooks/roles/ceph-fuse/templates/ceph.client.lain.keyring.j2: -------------------------------------------------------------------------------- 1 | [client.{{ceph_account}}] 2 | key = {{ceph_password}} 3 | -------------------------------------------------------------------------------- /playbooks/roles/ceph-fuse/templates/ceph.conf.j2: -------------------------------------------------------------------------------- 1 | [global] 2 | mon_host = {{ceph_mon_address}} 3 | -------------------------------------------------------------------------------- /playbooks/roles/ceph-fuse/templates/cephmon.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=cephmonitor 3 | After=network.target 4 | 5 | [Service] 6 | LimitNOFILE=65535 7 | ExecStart=/usr/bin/python /etc/ceph/plugins/ceph_mon.py 8 | Restart=on-failure 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/files/plugins/lain/deployd_monitor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | import requests 6 | import argparse 7 | import time 8 | import sys 9 | import socket 10 | from plugin import Plugin, GraphiteData 11 | from procutils import get_etcd_value 12 | 13 | 14 | class DeploydPlugin(Plugin): 15 | ''' 16 | The monitor plugin for deployd 17 | ''' 18 | _endpoint = socket.gethostname() 19 | _result = [] 20 | 21 | def __init__(self, step, deployd_port): 22 | self._step = step 23 | self._deployd_port = deployd_port 24 | 25 | def prepare_data(self): 26 | self._result = [] 27 | self._collect_deployd_debug_info() 28 | return self._result 29 | 30 | def _collect_deployd_debug_info(self): 31 | is_alive = 0 32 | try: 33 | domain = get_etcd_value("/lain/config/domain") 34 | resp = requests.get( 35 | "http://deployd.lain:%d/debug/vars" % self._deployd_port, timeout=1) 36 | if resp.status_code == 200: 37 | is_alive = 1 38 | except Exception: 39 | pass 40 | 41 | self._result.append( 42 | GraphiteData("lain.cluster.deployd.alive", 43 | self._endpoint, is_alive, self._step, "status")) 44 | 45 | 46 | if __name__ == "__main__": 47 | step = 30 48 | parser = argparse.ArgumentParser() 49 | parser.add_argument('--deployd-port', help="Deploy port", default=9003, type=int) 50 | args = parser.parse_args() 51 | deployd_plugin = DeploydPlugin(step, args.deployd_port) 52 | while True: 53 | deployd_plugin.report() 54 | sys.stdout.flush() 55 | time.sleep(step) 56 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/files/plugins/lain/docker_daemon_monitor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import collectd 4 | import time 5 | import requests 6 | 7 | 8 | class Plugin(object): 9 | DOCKER_PS_URL = "http://docker.lain:2375/containers/json" 10 | READ_INTERVAL = 60 # 60 seconds 11 | TIMEOUT = 5 # 5 seconds 12 | 13 | def init(self): 14 | collectd.info("docker_daemon_monitor plugin has been initialized.") 15 | 16 | def read(self): 17 | metric = collectd.Values() 18 | metric.plugin = "lain.cluster.docker_daemon" 19 | metric.plugin_instance = "docker_ps_time" 20 | metric.type = "val" 21 | start_at = time.time() 22 | requests.get( 23 | self.DOCKER_PS_URL, params={"limit": 1}, timeout=self.TIMEOUT) 24 | docker_ps_time = time.time() - start_at 25 | metric.values = [docker_ps_time] 26 | metric.dispatch() 27 | 28 | def shutdown(self): 29 | collectd.info("docker_daemon_monitor plugin has been shutdown.") 30 | 31 | 32 | docker_daemon = Plugin() 33 | 34 | if __name__ != "__main__": 35 | collectd.register_init(docker_daemon.init) 36 | collectd.register_read(docker_daemon.read, docker_daemon.READ_INTERVAL) 37 | collectd.register_shutdown(docker_daemon.shutdown) 38 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/files/plugins/lain/lainlet_monitor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | from plugin import Plugin, GraphiteData 5 | import requests 6 | import socket 7 | import time 8 | import sys 9 | import argparse 10 | 11 | 12 | class LainletPlugin(Plugin): 13 | ''' 14 | The monitor plugin for lainlet 15 | ''' 16 | _endpoint = socket.gethostname() 17 | _result = [] 18 | 19 | def __init__(self, step, lainlet_port): 20 | self._step = step 21 | self._debug_url = "http://lainlet.lain:%d/debug" % (lainlet_port) 22 | 23 | def prepare_data(self): 24 | self._result = [] 25 | self._collect_lainlet_debug_info() 26 | return self._result 27 | 28 | def _collect_lainlet_debug_info(self): 29 | connections = 0 30 | goroutines = 0 31 | try: 32 | resp = requests.get(self._debug_url, timeout=1) 33 | data = resp.json() 34 | connections = data['connections'] 35 | goroutines = data['goroutines'] 36 | except Exception: 37 | pass 38 | 39 | self._result.append( 40 | GraphiteData("lain.cluster.lainlet.goroutines", 41 | self._endpoint, goroutines, self._step, "val")) 42 | self._result.append( 43 | GraphiteData("lain.cluster.lainlet.connections", 44 | self._endpoint, connections, self._step, "val")) 45 | 46 | 47 | if __name__ == "__main__": 48 | parser = argparse.ArgumentParser() 49 | parser.add_argument("--lainlet-port", help="Lainlet port", 50 | default=9001, type=int) 51 | args = parser.parse_args() 52 | step = 30 53 | lainlet_plugin = LainletPlugin(step, args.lainlet_port) 54 | while True: 55 | lainlet_plugin.report() 56 | sys.stdout.flush() 57 | time.sleep(step) 58 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/files/plugins/lain/plugin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class Plugin(object): 5 | ''' 6 | The base class for plugins 7 | All you need is just to extend it, overwrite prepare_data() method, 8 | and call report() method in __main__. 9 | ''' 10 | 11 | def report(self): 12 | ''' 13 | Report your prepared_data() to destination system 14 | ''' 15 | data = self.prepare_data() 16 | self._report_to_graphite(data) 17 | 18 | def verbose(self): 19 | ''' 20 | Show the data to the cluster administrators 21 | ''' 22 | data = self.prepare_data() 23 | self._report_to_console(data) 24 | 25 | def prepare_data(self): 26 | ''' 27 | Prepare your data. Return a list of DataItem(or its subclass) instances 28 | ''' 29 | return [] 30 | 31 | def _report_to_graphite(self, data): 32 | data_format = 'PUTVAL "%s/%s/%s" interval=%s N:%s' 33 | for item in data: 34 | data_map = item.format_data() 35 | print data_format % (data_map["endpoint"], data_map["metric"], 36 | data_map["type"], data_map["step"], 37 | data_map["value"]) 38 | 39 | def _report_to_console(self, data): 40 | data_format = '%s %s %s' 41 | for item in data: 42 | data_map = item.format_data() 43 | val = data_map["value"] 44 | if data_map["type"] == "status": 45 | val = "OK" if data_map["value"] == 1 else "FAILED" 46 | print data_format % (data_map["endpoint"], data_map["metric"], val) 47 | 48 | 49 | class DataItem(object): 50 | ''' 51 | The base class of data items. 52 | If new monitor system is applied, we should support both new and old data 53 | format. So extend it and overwrite format_data() to satisfy new system, and 54 | modify old subclasses to make it capatible for old plugins 55 | ''' 56 | 57 | def format_data(self): 58 | ''' 59 | Formatting the data, return a dict 60 | ''' 61 | return {} 62 | 63 | 64 | class GraphiteData(DataItem): 65 | _metric = "" 66 | _endpoint = "" 67 | _value = "" 68 | _step = "" 69 | _type = "" 70 | 71 | def __init__(self, metric, endpoint, 72 | value, step, type): 73 | self._metric = metric 74 | self._endpoint = endpoint 75 | self._value = value 76 | self._step = step 77 | self._type = type 78 | 79 | def format_data(self): 80 | return { 81 | "metric": self._metric, 82 | "endpoint": self._endpoint, 83 | "value": self._value, 84 | "step": self._step, 85 | "type": self._type, 86 | } 87 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/files/plugins/lain/procutils.py: -------------------------------------------------------------------------------- 1 | import psutil 2 | import subprocess 3 | 4 | 5 | SIZE_KB = 1 << 10 6 | SIZE_MB = 1 << 20 7 | SIZE_GB = 1 << 30 8 | SIZE_TB = 1 << 40 9 | 10 | SCALE_MAP = { 11 | "KB": SIZE_KB, 12 | "MB": SIZE_MB, 13 | "GB": SIZE_GB, 14 | "TB": SIZE_TB, 15 | "B": 1, 16 | } 17 | 18 | 19 | def get_proc(proc_name): 20 | ''' 21 | Get the process info by name 22 | ''' 23 | for p in psutil.process_iter(): 24 | if p.name() == proc_name: 25 | return p 26 | return None 27 | 28 | 29 | def get_etcd_value(key): 30 | out = "" 31 | try: 32 | out = subprocess.check_output(['etcdctl', 'get', key]) 33 | except subprocess.CalledProcessError: 34 | pass 35 | return out.strip() 36 | 37 | 38 | def convert_to_byte(value, scale): 39 | if scale in SCALE_MAP: 40 | return float(value) * SCALE_MAP[scale] 41 | else: 42 | return 0 43 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/files/plugins/lain/rebellion_monitor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import collectd 4 | import requests 5 | 6 | 7 | class Plugin(object): 8 | DOCKER_URL_PREFIX = "http://docker.lain:2375" 9 | READ_INTERVAL = 60 # 60 seconds 10 | TIMEOUT = 6 # 6 seconds 11 | NAME = "lain.cluster.node" 12 | 13 | def init(self): 14 | collectd.info("rebellion_monitor plugin has been initialized.") 15 | 16 | def read(self): 17 | try: 18 | params = {"filters": '{"name": ["rebellion.service"]}'} 19 | containers = requests.get( 20 | "{}/containers/json".format(self.DOCKER_URL_PREFIX), 21 | params=params, 22 | timeout=self.TIMEOUT).json() 23 | metric = collectd.Values() 24 | metric.plugin = self.NAME 25 | metric.plugin_instance = "rebellion_service" 26 | metric.type = "val" 27 | metric.values = [len(containers)] 28 | metric.dispatch() 29 | except Exception as e: 30 | collectd.error( 31 | "rebellion_monitor.read() failed, exception: {}".format(e)) 32 | 33 | def shutdown(self): 34 | collectd.info("rebellion_monitor plugin has been shutdown.") 35 | 36 | 37 | if __name__ != "__main__": 38 | rebellion_monitor = Plugin() 39 | collectd.register_init(rebellion_monitor.init) 40 | collectd.register_read(rebellion_monitor.read, 41 | rebellion_monitor.READ_INTERVAL) 42 | collectd.register_shutdown(rebellion_monitor.shutdown) 43 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/files/types/lain.db: -------------------------------------------------------------------------------- 1 | net value:COUNTER:0:U 2 | status value:GAUGE:0:1 3 | val value:GAUGE:-1:U 4 | percent value:GAUGE:0:1 5 | time_diff value:COUNTER:0:U 6 | blkio value:COUNTER:0:U 7 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: restart collectd 2 | service: name=collectd state=restarted 3 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: os-dependent-vars 3 | - role: config 4 | - role: libraries 5 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - set_fact: collectd_version="5.5.1" 2 | 3 | - name: install collectd(CentOS) 4 | yum: pkg=collectd-{{ collectd_version }} 5 | when: ansible_distribution=="CentOS" 6 | 7 | - name: install collectd(Ubuntu) 8 | apt: name=collectd=5.5.1-1build2 state=installed 9 | when: ansible_distribution=="Ubuntu" 10 | 11 | - name: install collectd plugins and dependencies 12 | yum: pkg={{ item }} 13 | with_items: 14 | - collectd-ping-{{ collectd_version }} 15 | - gcc 16 | - python-devel 17 | - python-pip 18 | when: ansible_distribution=="CentOS" 19 | 20 | - name: install python libs 21 | command: pip install docker-py psutil==4.1.0 22 | environment: 23 | http_proxy: "{{ http_proxy }}" 24 | https_proxy: "{{ https_proxy }}" 25 | 26 | # lastest collectd without disk pkg 27 | - name: install collectd plugins 28 | yum: pkg={{ item }} 29 | with_items: 30 | - collectd-disk-{{ collectd_version }} 31 | ignore_errors: yes 32 | when: ansible_distribution=="CentOS" 33 | 34 | - name: create collectd user 35 | user: name=collectd createhome=no append=yes groups=docker system=yes 36 | 37 | - name: deploy collectd main config 38 | template: src=collectd.conf.j2 dest={{ collectd_conf_path}} 39 | notify: restart collectd 40 | 41 | - name: ensure plugins dir 42 | file: path=/var/lib/collectd/plugins state=directory 43 | 44 | - name: deploy collectd types 45 | copy: src=types dest=/var/lib/collectd/ 46 | notify: restart collectd 47 | 48 | - name: deploy collectd lain plugins 49 | copy: src=plugins dest=/var/lib/collectd/ mode=0755 50 | notify: restart collectd 51 | 52 | - name: ensure conf dir 53 | file: path={{collectd_conf_dir}} state=directory 54 | 55 | - name: deploy collectd plugins config 56 | template: src={{ item }}.j2 dest={{collectd_conf_dir}}/{{ item }} 57 | with_items: 58 | - docker_daemon_monitor.conf 59 | - lain.conf 60 | - node_monitor.conf 61 | - rebellion_monitor.conf 62 | notify: restart collectd 63 | 64 | - name: ensure collectd running 65 | service: name=collectd enabled=yes state=started 66 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/templates/docker_daemon_monitor.conf.j2: -------------------------------------------------------------------------------- 1 | LoadPlugin python 2 | # ... 3 | 4 | ModulePath "/var/lib/collectd/plugins/lain" 5 | LogTraces true 6 | Interactive false 7 | Import "docker_daemon_monitor" 8 | 9 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/templates/lain.conf.j2: -------------------------------------------------------------------------------- 1 | TypesDB "/var/lib/collectd/types/lain.db" 2 | 3 | 4 | Exec "collectd:docker" "/var/lib/collectd/plugins/lain/lain_docker.py" "--domain" "{{domain}}" 5 | Exec "collectd:docker" "/var/lib/collectd/plugins/lain/cluster_monitor.py" "--swarm-manager-port" "{{ swarm_manager_port }}" "--docker-port" "{{ docker_port }}" "--ceph-fuse" "{{ client_mountpoint }}" 6 | Exec "collectd:docker" "/var/lib/collectd/plugins/lain/lainlet_monitor.py" "--lainlet-port" "{{ lainlet_port }}" 7 | Exec "collectd:docker" "/var/lib/collectd/plugins/lain/deployd_monitor.py" "--deployd-port" "{{ deployd_port }}" 8 | 9 | 10 | 11 | ProcessMatch "etcd" "/usr/bin/etcd" 12 | ProcessMatch "lainlet" "/usr/bin/lainlet" 13 | ProcessMatch "networkd" "/usr/bin/networkd" 14 | ProcessMatch "deployd" "/usr/bin/deployd" 15 | ProcessMatch "docker" "dockerd" 16 | ProcessMatch "rebellion" "rebellion" 17 | ProcessMatch "filebeat" "filebeat" 18 | ProcessMatch "rsyslogd" "/usr/sbin/rsyslogd" 19 | ProcessMatch "consul" "/usr/bin/consul" 20 | 21 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/templates/node_monitor.conf.j2: -------------------------------------------------------------------------------- 1 | LoadPlugin python 2 | # ... 3 | 4 | ModulePath "/var/lib/collectd/plugins/lain" 5 | LogTraces true 6 | Interactive false 7 | Import "node_monitor" 8 | 9 | -------------------------------------------------------------------------------- /playbooks/roles/collectd/templates/rebellion_monitor.conf.j2: -------------------------------------------------------------------------------- 1 | LoadPlugin python 2 | # ... 3 | 4 | ModulePath "/var/lib/collectd/plugins/lain" 5 | LogTraces true 6 | Interactive false 7 | Import "rebellion_monitor" 8 | 9 | -------------------------------------------------------------------------------- /playbooks/roles/console-deploy/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: check if app already deployed 2 | command: etcdctl ls /lain/deployd/pod_groups/{{ app }} 3 | register: result 4 | ignore_errors: yes 5 | changed_when: False 6 | 7 | - name: waiting for console ready to deploy 8 | shell: "curl -f http://console.lain/api/v1/repos/" 9 | register: console_ready 10 | until: console_ready.rc == 0 11 | retries: 50 12 | delay: 5 13 | changed_when: False 14 | when: result|failed 15 | 16 | - name: reposit app 17 | post_json: 18 | url: http://console.lain/api/v1/repos/ 19 | body: 20 | appname: "{{ app }}" 21 | header: 22 | access-token: "{{ access_token|default('unknown') }}" 23 | when: result|failed 24 | 25 | - name: deploy app 26 | post_json: 27 | url: http://console.lain/api/v1/apps/ 28 | body: 29 | appname: "{{ app }}" 30 | header: 31 | access-token: "{{ access_token|default('unknown') }}" 32 | when: result|failed 33 | 34 | - name: check app deployed 35 | shell: "curl -s -H \"access-token: {{ access_token|default('unknown') }}\" console.lain/api/v1/apps/{{ app }}/ | python -c \"import json, sys; print json.load(sys.stdin)['app']['procs'][0]['pods'][0]['containerid']\"" 36 | register: container_id 37 | until: container_id|success 38 | retries: 50 39 | delay: 5 40 | changed_when: False 41 | -------------------------------------------------------------------------------- /playbooks/roles/console/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: prepare volume dirs 2 | file: path={{ lain_data_dir }}/volumes/console/console.web.web/1/{{ item }} state=directory 3 | with_items: 4 | - externalbin 5 | 6 | - name: copy volume files 7 | copy: src={{ item.src }} dest={{ lain_data_dir }}/volumes/console/console.web.web/1/{{ item.dest }} mode={{ item.mode }} 8 | with_items: 9 | - src: /usr/bin/calicoctl 10 | dest: externalbin/calicoctl 11 | mode: "0755" 12 | 13 | # vim: set filetype=ansible.yaml: 14 | -------------------------------------------------------------------------------- /playbooks/roles/consul/files/consul.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Consul Agent 3 | After=network.target 4 | 5 | [Service] 6 | Restart=on-failure 7 | ExecStart=/usr/bin/consul agent -config-file=/etc/consul/consul.json 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /playbooks/roles/consul/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: copy consul binary file 2 | copy: 3 | src: bin/consul 4 | dest: /usr/bin/consul 5 | force: yes 6 | mode: a+x 7 | 8 | - name: mkdir /etc/consul 9 | file: path=/etc/consul state=directory 10 | 11 | - name: generate consul.json 12 | template: 13 | src: consul.json.j2 14 | dest: /etc/consul/consul.json 15 | 16 | - name: copy consul.service 17 | copy: 18 | src: consul.service 19 | dest: /etc/systemd/system/consul.service 20 | 21 | - name: systemctl daemon-reload 22 | command: systemctl daemon-reload 23 | 24 | - name: systemctl restart consul && systemctl enable consul 25 | service: 26 | name: consul 27 | state: restarted 28 | enabled: yes 29 | -------------------------------------------------------------------------------- /playbooks/roles/consul/templates/consul.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | {% if is_consul_server %} 3 | "bootstrap_expect": {{consul_servers|length}}, 4 | {% endif %} 5 | "retry_join": [{% for ip in consul_servers | difference(ansible_all_ipv4_addresses) %}"{{ ip }}"{% if not loop.last %}, {% endif %}{% endfor %}], 6 | "data_dir": "/var/lib/consul", 7 | "server": {{ "true" if is_consul_server else "false" }}, 8 | "bind_addr": "{{ node_ip }}", 9 | "client_addr": "0.0.0.0", 10 | "disable_update_check": true 11 | } 12 | -------------------------------------------------------------------------------- /playbooks/roles/cronjob/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: backup old crontab 2 | shell: crontab -l > ~/backup.cron 3 | ignore_errors: yes 4 | 5 | - name: copy image-clean script 6 | copy: 7 | src: clean_lainnode_image.py 8 | dest: ~/clean_lainnode_image.py 9 | mode: 0777 10 | 11 | - name: add clean lain node docker images cronjob 12 | cron: 13 | name: "clean docker images" 14 | minute: "0" 15 | hour: "1" 16 | job: "~/clean_lainnode_image.py --debug &> /dev/nulll" 17 | -------------------------------------------------------------------------------- /playbooks/roles/deployd/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config -------------------------------------------------------------------------------- /playbooks/roles/deployd/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: copy deployd binary file 2 | copy: 3 | src: deployd 4 | dest: /usr/bin/deployd 5 | force: yes 6 | mode: a+x 7 | 8 | - name: generate deployd.service 9 | template: 10 | src: deployd.service.j2 11 | dest: /etc/systemd/system/deployd.service 12 | 13 | - name: systemctl daemon-reload 14 | command: systemctl daemon-reload 15 | 16 | - name: systemctl restart deployd && systemctl enable deployd 17 | service: 18 | name: deployd 19 | state: restarted 20 | enabled: yes 21 | -------------------------------------------------------------------------------- /playbooks/roles/deployd/templates/deployd.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=deployd 3 | After=network.target 4 | 5 | [Service] 6 | LimitNOFILE=65535 7 | ExecStart=/usr/bin/deployd \ 8 | -web=:{{ deployd_port }} \ 9 | -etcd=http://etcd.lain:{{ etcd_client_port }} \ 10 | -swarm=tcp://swarm.lain:{{ swarm_manager_port }} \ 11 | -advertise={{ node_ip }}:{{ deployd_port }} \ 12 | -debug 13 | Restart=on-failure 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /playbooks/roles/docker-netstat/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | -------------------------------------------------------------------------------- /playbooks/roles/docker-netstat/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: copy docker-netstat 2 | copy: 3 | src: bin/docker-netstat 4 | dest: /usr/bin/docker-netstat 5 | force: yes 6 | mode: a+x 7 | 8 | - name: generate docker-netstat.service 9 | template: src=docker-netstat.service.j2 dest=/lib/systemd/system/docker-netstat.service 10 | 11 | - name: systemctl daemon-reload 12 | command: systemctl daemon-reload 13 | 14 | - name: enable and restart docker-netstat.service 15 | service: 16 | name: docker-netstat.service 17 | state: restarted 18 | enabled: yes 19 | -------------------------------------------------------------------------------- /playbooks/roles/docker-netstat/templates/docker-netstat.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Docker Netstat 3 | Requires=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | Restart=on-failure 8 | ExecStart=/usr/bin/docker-netstat -graphite {{ graphite_vip }}:{{ graphite_port }} 9 | 10 | [Install] 11 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /playbooks/roles/docker-upgrade/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: docker-version 3 | - role: swarm 4 | -------------------------------------------------------------------------------- /playbooks/roles/docker-upgrade/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd 2 | command: systemctl daemon-reload 3 | 4 | - name: restart deployd 5 | service: name=deployd state=started 6 | 7 | 8 | -------------------------------------------------------------------------------- /playbooks/roles/docker-version/defaults/main.yaml: -------------------------------------------------------------------------------- 1 | docker_version: 1.12.1-1.el7.centos -------------------------------------------------------------------------------- /playbooks/roles/docker-version/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for docker 2 | command: systemctl daemon-reload 3 | 4 | - name: restart docker 5 | service: name=docker state=restarted 6 | 7 | - name: stop docker 8 | service: name=docker enabled=yes state=stopped -------------------------------------------------------------------------------- /playbooks/roles/docker-version/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: prepare 3 | -------------------------------------------------------------------------------- /playbooks/roles/docker-version/tasks/devicemapper.yaml: -------------------------------------------------------------------------------- 1 | - shell: lvdisplay -c | grep {{ volume_group }} | grep /data | cut -d ":" -f 1 2 | register: result 3 | 4 | - set_fact: 5 | devicemapper_data_dir: "{{ result.stdout|trim }}" 6 | 7 | - shell: lvdisplay -c | grep {{ volume_group }} | grep /metadata | cut -d ":" -f 1 8 | register: result 9 | 10 | - set_fact: 11 | devicemapper_metadata_dir: "{{ result.stdout|trim }}" 12 | -------------------------------------------------------------------------------- /playbooks/roles/docker-version/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: get stat of old deployd 2 | stat: path=/usr/bin/deployd 3 | register: deployd 4 | ignore_errors: yes 5 | 6 | - name: stop deployd service 7 | service: name=deployd state=stopped 8 | when: deployd|success 9 | 10 | - name: stop running docker 11 | service: name=docker enabled=yes state=stopped 12 | 13 | - name: uninstall exists docker 14 | yum: name={{ item }} state=absent 15 | with_items: 16 | - docker-engine-selinux.noarch 17 | - docker-engine.x86_64 18 | 19 | # TODO: support more distro 20 | - name: install(update) lvm2 and device-mapper 21 | yum: name=lvm2,device-mapper state=latest 22 | when: ansible_distribution == "CentOS" 23 | 24 | - name: install docker from OS package manage system 25 | yum: pkg={{ item }} 26 | with_items: 27 | - "/tmp/lain/docker-engine-selinux-{{ docker_version }}.noarch.rpm" 28 | - "/tmp/lain/docker-engine-{{ docker_version }}.x86_64.rpm" 29 | register: pkg 30 | when: ansible_distribution == "CentOS" 31 | 32 | - name: install docker for ubuntu 33 | apt: 34 | deb: "/tmp/lain/docker-ce_17.09.1_ubuntu_{{ansible_distribution_version}}_amd64.deb" 35 | when: ansible_distribution == "Ubuntu" 36 | 37 | - name: remove default Docker keys, avoid issues with Swarm 38 | file: path=/etc/docker/key.json state=absent 39 | when: adding_node_mode is defined 40 | 41 | - set_fact: 42 | docker_device: "{{node_info['docker_device']}}" 43 | when: docker_device == "" and node_info is defined 44 | 45 | - include: devicemapper.yaml 46 | when: docker_device != "" and ansible_distribution == "CentOS" 47 | 48 | # Restarting docker is a big deal. User must allow it explicitly. 49 | - name: render docker config for test 50 | template: src=docker.j2 dest=/tmp/docker.conf 51 | - name: get stat of rendered docker config 52 | stat: path=/tmp/docker.conf 53 | register: new_docker_conf_stat 54 | - name: get stat of current docker config 55 | stat: path=/etc/systemd/system/docker.service 56 | register: current_docker_conf_stat 57 | 58 | - name: config docker 59 | template: src=docker.j2 dest=/etc/systemd/system/docker.service 60 | notify: 61 | - reload systemd for docker 62 | - restart docker 63 | - meta: flush_handlers 64 | 65 | - name: set docker graph dir 66 | file: path={{ docker_graph_dir }} state=directory 67 | 68 | - name: ensure docker started 69 | service: name=docker state=started 70 | 71 | - name: wait for docker daemon start, again 72 | command: docker version -f "\{\{.Server.Version\}\}" 73 | register: current_docker_version 74 | until: current_docker_version.stdout 75 | retries: 50 76 | delay: 5 77 | changed_when: False 78 | -------------------------------------------------------------------------------- /playbooks/roles/docker-version/templates/docker.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Docker Application Container Engine 3 | Documentation=https://docs.docker.com 4 | After=network.target 5 | [Service] 6 | Type=notify 7 | Environment=GOTRACEBACK=crash DOCKER_CERT_PATH=/etc/docker 8 | ExecStart=/usr/bin/docker daemon \ 9 | {% if docker_device != "" %} 10 | --storage-driver=devicemapper \ 11 | --storage-opt dm.datadev={{ devicemapper_data_dir }} \ 12 | --storage-opt dm.metadatadev={{ devicemapper_metadata_dir }} \ 13 | {% endif -%} 14 | {% if registry_mirror is defined %} 15 | --registry-mirror={{ registry_mirror }} \ 16 | --insecure-registry={{ registry_mirror|replace('http://', '')|replace('https://', '') }} \ 17 | {% endif -%} 18 | --insecure-registry=registry.{{ domain }} \ 19 | --dns={{ node_ip }} \ 20 | --selinux-enabled \ 21 | --graph {{ docker_graph_dir }} \ 22 | --host unix:///var/run/docker.sock \ 23 | --host tcp://{{ node_ip }}:{{ docker_port }} \ 24 | --log-driver=none \ 25 | --cluster-store=etcd://{{ node_ip }}:{{ etcd_client_port }} 26 | 27 | MountFlags=slave 28 | LimitNOFILE=1048576 29 | LimitNPROC=1048576 30 | LimitCORE=infinity 31 | 32 | [Install] 33 | WantedBy=multi-user.target 34 | -------------------------------------------------------------------------------- /playbooks/roles/docker/files/bootstrap-ubuntu-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Created by Yu Yang on 2017-12-11 4 | # 5 | if ! which dockerd 2>&1 >/dev/null; then 6 | sudo apt-get remove docker docker-engine docker.io 7 | sudo apt-get update 8 | sudo apt-get -y install \ 9 | linux-image-extra-$(uname -r) \ 10 | linux-image-extra-virtual 11 | 12 | sudo apt-get -y install \ 13 | apt-transport-https \ 14 | ca-certificates \ 15 | curl \ 16 | software-properties-common 17 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 18 | sudo apt-key fingerprint 0EBFCD88 19 | sudo add-apt-repository \ 20 | "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ 21 | $(lsb_release -cs) \ 22 | stable" 23 | sudo apt-get update 24 | sudo apt-get install -y docker-ce=17.09.1~ce-0~ubuntu 25 | fi 26 | -------------------------------------------------------------------------------- /playbooks/roles/docker/files/docker-enter: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker exec -it $1 env TERM=$TERM bash 4 | -------------------------------------------------------------------------------- /playbooks/roles/docker/files/docker-enter-comp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | _docker_enter() 4 | { 5 | local cur prev opts 6 | COMPREPLY=() 7 | cur="${COMP_WORDS[COMP_CWORD]}" 8 | prev="${COMP_WORDS[COMP_CWORD-1]}" 9 | opts=`docker ps --format "{{.Names}}" --filter status=running` 10 | 11 | COMPREPLY=( $( compgen -W "${opts}" -- "$cur" ) ) 12 | } 13 | 14 | complete -F _docker_enter docker-enter 15 | 16 | # ex: ts=4 sw=4 et filetype=sh 17 | -------------------------------------------------------------------------------- /playbooks/roles/docker/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for docker 2 | command: systemctl daemon-reload 3 | 4 | - name: restart docker 5 | service: name=docker state=restarted 6 | -------------------------------------------------------------------------------- /playbooks/roles/docker/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: os-dependent-vars 3 | - role: config 4 | - role: libraries 5 | - role: rsyslog 6 | - role: etcd 7 | - role: binary 8 | - role: firewall 9 | - role: networkd 10 | -------------------------------------------------------------------------------- /playbooks/roles/docker/tasks/devicemapper.yaml: -------------------------------------------------------------------------------- 1 | - name: check the size(bytes) of block device 2 | shell: blockdev --getsize64 "{{ docker_device }}" 3 | register: size_result 4 | 5 | - set_fact: 6 | size: "{{ size_result.stdout|int // 1024 // 1024 // 1024 }}" 7 | 8 | - name: check if size big enough 9 | fail: msg="the given block device's size is too small" 10 | when: size < 10 11 | 12 | - name: check if the physical volume having been created 13 | shell: pvdisplay -c {{ docker_device }} 14 | register: result 15 | ignore_errors: true 16 | 17 | - name: create physical volume for docker devicemapper storage 18 | command: pvcreate "{{ docker_device }}" 19 | when: result|failed 20 | 21 | - name: check if the volume group having been created 22 | command: vgdisplay "{{ volume_group }}" 23 | register: result 24 | ignore_errors: true 25 | 26 | - name: create volume group 27 | command: vgcreate "{{ volume_group }}" "{{ docker_device }}" 28 | when: result | failed 29 | 30 | - name: check if the logical volume for data having been created 31 | shell: lvdisplay -c | grep {{ volume_group }} | grep /data 32 | register: result 33 | ignore_errors: true 34 | 35 | - name: create logical volume for data 36 | command: lvcreate -l95%VG -n data "{{ volume_group }}" 37 | when: result|failed 38 | 39 | - shell: lvdisplay -c | grep {{ volume_group }} | grep /data | cut -d ":" -f 1 40 | register: result 41 | 42 | - set_fact: 43 | devicemapper_data_dir: "{{ result.stdout|trim }}" 44 | 45 | - name: check if the logical volume for data having been created 46 | shell: lvdisplay -c | grep {{ volume_group }} | grep /metadata 47 | register: result 48 | ignore_errors: true 49 | 50 | - name: create logical volume for metadata 51 | command: lvcreate -l100%FREE -n metadata "{{ volume_group }}" 52 | when: result|failed 53 | 54 | - shell: lvdisplay -c | grep {{ volume_group }} | grep /metadata | cut -d ":" -f 1 55 | register: result 56 | 57 | - set_fact: 58 | devicemapper_metadata_dir: "{{ result.stdout|trim }}" 59 | -------------------------------------------------------------------------------- /playbooks/roles/docker/templates/daemon.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | {% if ansible_distribution == "CentOS" %} 3 | {% if docker_device != "" %} 4 | "storage-driver": "devicemapper", 5 | "storage-opts": ["dm.datadev={{ devicemapper_data_dir }}","dm.metadatadev={{ devicemapper_metadata_dir }}"], 6 | {% endif -%} 7 | "selinux-enabled": true, 8 | {% endif -%} 9 | 10 | {% if registry_mirror is defined %} 11 | "registry-mirrors": ["{{ registry_mirror }}"], 12 | {% endif -%} 13 | {% if node_labels is defined %} 14 | "labels": [{% for label in node_labels %}"{{ label }}"{% if not loop.last %}, {% endif %}{% endfor %}], 15 | {% endif -%} 16 | "insecure-registries": ["registry.{{ domain }}"{% if registry_mirror is defined %},"{{ registry_mirror|replace('http://', '')|replace('https://', '') }}"{% endif -%}], 17 | "dns": ["{{ node_ip }}"], 18 | "graph": "{{ docker_graph_dir }}", 19 | "hosts": ["unix:///var/run/docker.sock","tcp://{{ node_ip }}:{{ docker_port }}"], 20 | "log-driver": "none", 21 | "cluster-store": "consul://127.0.0.1:8500", 22 | "labels": ["group=default"] 23 | } 24 | -------------------------------------------------------------------------------- /playbooks/roles/drift-warm-up/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - libraries 3 | -------------------------------------------------------------------------------- /playbooks/roles/drift-warm-up/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: check whether images exist 2 | docker_check_images: 3 | images: "{{ to_drift_images }}" 4 | when: node_name == target_node 5 | ignore_errors: yes 6 | register: check 7 | changed_when: False 8 | 9 | - name: pull images when not exist (this may take minutes) 10 | docker_pull_image: image={{ item }} 11 | with_items: "{{ to_drift_images }}" 12 | when: node_name == target_node and (check|failed) 13 | -------------------------------------------------------------------------------- /playbooks/roles/drift/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | -------------------------------------------------------------------------------- /playbooks/roles/drift/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - set_fact: 2 | drift_vars: "{{ lookup('template', var_file) }}" 3 | 4 | - name: create directory on target node 5 | file: path="{{item}}" state=directory 6 | with_items: "{{ drift_vars['volumes'] }}" 7 | when: node_name == target_node 8 | 9 | - name: write rsyncd password file 10 | shell: etcdctl get /lain/config/rsyncd_secrets > /tmp/pass 11 | when: node_name == target_node 12 | 13 | - name: chmod password file 14 | file: path=/tmp/pass state=file mode=0600 group=root owner=root 15 | when: node_name == target_node 16 | 17 | - name: pull data from source node 18 | shell: rsync -az --password-file /tmp/pass rsync://"{{rsync_auth_user}}@{{from_ip}}/{{rsync_module}}{{ item[18:] }}/" "{{ item }}"/ 19 | with_items: "{{ drift_vars['volumes'] }}" 20 | when: node_name == target_node 21 | 22 | - name: remove the password file 23 | file: path=/tmp/pass state=absent 24 | -------------------------------------------------------------------------------- /playbooks/roles/etcd-backup/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for etcd_backup 2 | command: systemctl daemon-reload 3 | 4 | - name: restart etcd_backup 5 | service: name=etcd_backup state=restarted 6 | -------------------------------------------------------------------------------- /playbooks/roles/etcd-backup/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: libraries 4 | -------------------------------------------------------------------------------- /playbooks/roles/etcd-backup/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: ship etcd_backup binary 2 | copy: src=plugins/lain/etcd_backup dest=/usr/bin/etcd_backup 3 | when: is_lain_manager 4 | 5 | - name: make etcd_backup service executable 6 | shell: chmod +x /usr/bin/etcd_backup 7 | when: is_lain_manager 8 | 9 | - name: config etcd_backup service 10 | template: src=etcd_backup.service.j2 dest=/etc/systemd/system/etcd_backup.service 11 | notify: 12 | - reload systemd for etcd_backup 13 | when: is_lain_manager 14 | 15 | - name: start etcd_backup 16 | shell: systemctl restart etcd_backup 17 | when: is_lain_manager 18 | 19 | - name: ensure etcd_backup 20 | service: name=etcd_backup state=started enabled=yes 21 | when: is_lain_manager 22 | -------------------------------------------------------------------------------- /playbooks/roles/etcd-backup/templates/etcd_backup.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=etcd_backup 3 | After=network.target 4 | 5 | [Service] 6 | User=root 7 | LimitNOFILE=65535 8 | Environment='HOSTNAME={{ node_name }}' 9 | ExecStart=/usr/bin/etcd_backup --backup_dir={{ etcd_backup_dir }}/ 10 | Restart=on-failure 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /playbooks/roles/etcd-moosefs/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for etcd_backup mount 2 | command: systemctl daemon-reload 3 | 4 | - name: mount etcd_backup directory 5 | service: name=data-lain-etcd_backup.mount state=restarted 6 | -------------------------------------------------------------------------------- /playbooks/roles/etcd-moosefs/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | -------------------------------------------------------------------------------- /playbooks/roles/etcd-moosefs/tasks/build.yaml: -------------------------------------------------------------------------------- 1 | - fail: msg="moosefs not exist" 2 | when: mfsmaster == "" 3 | - fail: msg="unknown etcd_backup's dir" 4 | when: etcd_backup_dir == "" 5 | 6 | - stat: path="{{ etcd_mfsdir }}" 7 | register: result 8 | - fail: msg="etcd_backup datadir {{ etcd_mfsdir }} having been exist on moosefs, check and delete it by yourself" 9 | when: result.stat.isdir is defined 10 | 11 | - name: create etcd directory on moosefs 12 | file: path="{{ etcd_mfsdir }}" state=directory 13 | 14 | - name: stop etcd_backup service 15 | service: name=etcd_backup state=stopped 16 | 17 | - file: path="{{ etcd_backup_dir }}.bak" state=absent 18 | - name: backup etcd data (may take minutes) 19 | command: cp -r "{{ etcd_backup_dir }}" "{{ etcd_backup_dir }}.bak" 20 | 21 | - name: move etcd data into moosefs 22 | shell: mv "{{ etcd_backup_dir }}/etcd" "{{ etcd_mfsdir }}/" 23 | 24 | # The .mount file must be var-lib-registry.mount, file name corresponds to /var/lib/registry, check `man systemd.mount` for detail 25 | - name: generate systemd.mount for etcd_backup 26 | template: src=data-lain-etcd_backup.mount.j2 dest=/etc/systemd/system/data-lain-etcd_backup.mount 27 | notify: 28 | - reload systemd for etcd_backup mount 29 | - mount etcd_backup directory 30 | - meta: flush_handlers 31 | 32 | - name: ensure etcd_backup data dir mounted onto moosefs and enable this service 33 | service: name=data-lain-etcd_backup.mount state=started enabled=yes 34 | 35 | - name: start etcd_backup service 36 | service: name=etcd_backup state=started enabled=yes -------------------------------------------------------------------------------- /playbooks/roles/etcd-moosefs/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - include: build.yaml 2 | when: is_lain_manager 3 | 4 | -------------------------------------------------------------------------------- /playbooks/roles/etcd-moosefs/templates/data-lain-etcd_backup.mount.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=bind etcd_backup data dir onto moosefs 3 | After=network.target remote-fs.target mfs.mount 4 | 5 | [Mount] 6 | What={{ etcd_mfsdir }} 7 | Where={{ etcd_backup_dir }} 8 | Type=none 9 | Options=bind 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /playbooks/roles/etcd/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for etcd 2 | command: systemctl daemon-reload 3 | 4 | - name: restart etcd 5 | service: name=etcd state=restarted 6 | -------------------------------------------------------------------------------- /playbooks/roles/etcd/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: libraries 4 | -------------------------------------------------------------------------------- /playbooks/roles/etcd/tasks/etcd-reset.yaml: -------------------------------------------------------------------------------- 1 | - name: add new etcd-member to cluster 2 | local_action: command etcdctl member add {{ node_name }} http://{{ node_ip }}:{{ etcd_peer_port }} 3 | when: adding_etcd_member 4 | 5 | - name: get etcd-member-id 6 | local_action: shell etcdctl member list | grep "name={{ node_name }}" | awk '{print substr($1, 0, length($1)-1)}' 7 | register: member_id 8 | failed_when: member_id.stdout == "" 9 | when: removing_etcd_member 10 | 11 | - name: remove etcd-member from cluster 12 | local_action: command etcdctl member remove {{ member_id.stdout }} 13 | register: result 14 | failed_when: "'500' in result.stderr" 15 | when: removing_etcd_member 16 | 17 | - name: stop etcd service 18 | service: name=etcd state=stopped 19 | when: delete_existing_etcd|bool 20 | ignore_errors: yes 21 | 22 | - name: remove old etcd data 23 | file: path={{ etcd_dir }} state=absent 24 | when: delete_existing_etcd|bool 25 | 26 | - name: create etcd data dir 27 | file: path={{ etcd_dir }} state=directory owner=etcd 28 | 29 | - name: prepare etcd config 30 | set_fact: 31 | etcd_cluster: "{% for node in etcd_members %}{{ node.name }}=http://{{ node.ip }}:{{ etcd_peer_port }}{% if not loop.last %},{% endif %}{% endfor %}" 32 | 33 | # now when etcd-cluster-resizing, all the nodes will cause template changed and restart etcd 34 | # TODO(chenyunfei) check whether is adding new lain-node 35 | - name: config etcd 36 | template: src=etcd.env.j2 dest={{ etcd_dir }}/env owner=etcd 37 | notify: 38 | - restart etcd 39 | 40 | - name: config etcd service 41 | template: src=etcd.service.j2 dest=/etc/systemd/system/etcd.service 42 | notify: 43 | - reload systemd for etcd 44 | - restart etcd 45 | 46 | - meta: flush_handlers 47 | -------------------------------------------------------------------------------- /playbooks/roles/etcd/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - file: path=/etc/etcd state=directory mode=0755 2 | 3 | - name: copy etcd 4 | copy: 5 | src: "{{ item.src }}" 6 | dest: "{{ item.dest }}" 7 | force: yes 8 | mode: "{{ item.mode }}" 9 | with_items: 10 | - { src: 'etcd', dest: '/usr/bin/etcd', mode: "0755" } 11 | - { src: 'etcdctl', dest: '/usr/bin/etcdctl', mode: "0755" } 12 | - { src: 'etcdctl_comp', dest: '/usr/share/bash-completion/completions/etcdctl', mode: "0644"} 13 | 14 | - name: create etcd user 15 | user: name=etcd createhome=no 16 | 17 | - name: get etcd member list 18 | local_action: shell etcdctl member list | awk '{print $2}' | awk '{print substr($1, 6)}' 19 | register: member_list 20 | failed_when: member_list.stdout == "" 21 | ignore_errors: yes 22 | changed_when: False 23 | 24 | - name: get wanted member list 25 | local_action: shell etcdctl ls /lain/nodes/etcd-members/ | awk -F '[/:]' '{print $5}' 26 | register: wanted_member_list 27 | failed_when: wanted_member_list.stdout == "" 28 | ignore_errors: yes 29 | changed_when: False 30 | 31 | - name: checking whether is adding or removing etcd member 32 | set_fact: 33 | adding_etcd_member: "{{ node_name not in member_list.stdout_lines and is_etcd_member}}" 34 | removing_etcd_member: "{{ node_name in member_list.stdout_lines and not is_etcd_member }}" 35 | when: member_list|success 36 | 37 | - name: decide whether to remove etcd data 38 | set_fact: 39 | delete_existing_etcd: true 40 | when: adding_etcd_member|bool or removing_etcd_member|bool 41 | 42 | - name: check if it is safe to change etcd(also backup etcd if yes) 43 | etcd_prepare_update: 44 | current_members: "{{ member_list.stdout_lines }}" 45 | wanted_members: "{{ wanted_member_list.stdout_lines }}" 46 | is_lain_manager: "{{ is_lain_manager }}" 47 | node_name: "{{node_name}}" 48 | ignore_errors: yes 49 | 50 | # begin to reset etcd 51 | - include: etcd-reset.yaml 52 | 53 | - name: ensure etcd service started 54 | service: name=etcd enabled=yes state=started 55 | 56 | - name: wait for etcd to be healthy 57 | shell: etcdctl cluster-health | grep "cluster is healthy" 58 | register: health_result 59 | until: health_result|success 60 | retries: 50 61 | delay: 5 62 | changed_when: False 63 | 64 | - name: wait for etcd is ready to write 65 | command: etcdctl set /lain/test test 66 | register: result 67 | until: result|success 68 | retries: 50 69 | delay: 5 70 | changed_when: False 71 | -------------------------------------------------------------------------------- /playbooks/roles/etcd/templates/etcd.env.j2: -------------------------------------------------------------------------------- 1 | ETCD_NAME="{{ node_name }}" 2 | ETCD_DATA_DIR="{{ etcd_dir }}/{{ node_name }}" 3 | ETCD_LISTEN_PEER_URLS="http://{{ node_ip }}:{{ etcd_peer_port }}" 4 | ETCD_LISTEN_CLIENT_URLS="http://localhost:{{ etcd_client_port }},http://{{ node_ip }}:{{ etcd_client_port }}" 5 | ETCD_ADVERTISE_CLIENT_URLS="http://{{ node_ip }}:{{ etcd_client_port }}" 6 | ETCD_INITIAL_CLUSTER="{{ etcd_cluster }}" 7 | {% if bootstrapping|bool %} 8 | ETCD_INITIAL_ADVERTISE_PEER_URLS="http://{{ node_ip }}:{{ etcd_peer_port }}" 9 | ETCD_INITIAL_CLUSTER_STATE="new" 10 | ETCD_INITIAL_CLUSTER_TOKEN="{{ cluster_token }}" 11 | {% elif is_etcd_member|bool %} 12 | ETCD_INITIAL_ADVERTISE_PEER_URLS="http://{{ node_ip }}:{{ etcd_peer_port }}" 13 | ETCD_INITIAL_CLUSTER_STATE="existing" 14 | {% else %} 15 | ETCD_PROXY="on" 16 | {% endif %} 17 | -------------------------------------------------------------------------------- /playbooks/roles/etcd/templates/etcd.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=etcd key-value store 3 | Documentation=https://github.com/coreos/etcd 4 | After=network.target 5 | 6 | [Service] 7 | User=etcd 8 | Type=notify 9 | LimitNOFILE=65535 10 | EnvironmentFile={{ etcd_dir }}/env 11 | ExecStart=/usr/bin/etcd 12 | Restart=on-failure 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /playbooks/roles/firewall/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: os-dependent-vars 3 | - role: config 4 | -------------------------------------------------------------------------------- /playbooks/roles/firewall/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | # firewall 2 | # TODO: should refine firewall configuration instead to disable it. 3 | - name: disable firewalld 4 | service: name=firewalld enabled=no state=stopped 5 | when: ansible_distribution=="CentOS" 6 | 7 | - name: mask firewalld 8 | shell: systemctl mask firewalld 9 | when: ansible_distribution=="CentOS" 10 | 11 | - name: install iptables 12 | package: 13 | name: "{{ iptables_pkt_name }}" 14 | state: present 15 | 16 | - name: enable iptables services 17 | service: name=iptables enabled=yes 18 | when: ansible_distribution == "CentOS" 19 | 20 | - name: deploy init iptables script 21 | template: src=iptables.sh.j2 dest=/tmp/iptables.sh 22 | 23 | - name: init iptables 24 | shell: bash /tmp/iptables.sh 25 | 26 | - name: deploy /etc/sysconfig/iptables 27 | template: src=iptables.save.j2 dest="{{ iptables_save_path }}" mode=600 28 | -------------------------------------------------------------------------------- /playbooks/roles/firewall/templates/iptables.save.j2: -------------------------------------------------------------------------------- 1 | *filter 2 | :INPUT ACCEPT [0:0] 3 | :FORWARD ACCEPT [0:0] 4 | :OUTPUT ACCEPT [0:0] 5 | COMMIT 6 | *nat 7 | :PREROUTING ACCEPT [0:0] 8 | :INPUT ACCEPT [0:0] 9 | :OUTPUT ACCEPT [0:0] 10 | :POSTROUTING ACCEPT [0:0] 11 | :lain-OUTPUT - [0:0] 12 | :lain-PREROUTING - [0:0] 13 | -A PREROUTING -j lain-PREROUTING 14 | -A OUTPUT -j lain-OUTPUT 15 | COMMIT 16 | -------------------------------------------------------------------------------- /playbooks/roles/firewall/templates/iptables.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | function chain_add() { 4 | iptables -n -L $1 &>/dev/null || iptables -N $1 5 | } 6 | 7 | function nat_chain_add() { 8 | iptables -t nat -n -L $1 &>/dev/null || iptables -t nat -N $1 9 | } 10 | 11 | function rule_append() { 12 | iptables -C $1 &>/dev/null || iptables -A $1 13 | } 14 | 15 | function nat_rule_append() { 16 | iptables -t nat -C $1 &>/dev/null || iptables -t nat -A $1 17 | } 18 | 19 | iptables -P INPUT ACCEPT 20 | iptables -P OUTPUT ACCEPT 21 | iptables -P FORWARD ACCEPT 22 | 23 | #iptables -D INPUT -j REJECT --reject-with icmp-host-prohibited || true 24 | #iptables -D FORWARD -j REJECT --reject-with icmp-host-prohibited || true 25 | 26 | #rule_append "INPUT -i lo -j ACCEPT" 27 | #rule_append "INPUT -i docker+ -j ACCEPT" 28 | #rule_append "INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT" 29 | #rule_append "INPUT -s {{ node_network }} -j ACCEPT" 30 | #rule_append "INPUT -s {{ calico_default_network }} -j ACCEPT" 31 | #rule_append "INPUT -p icmp -j ACCEPT" 32 | #rule_append "INPUT -p tcp --dport {{ ssh_port }} -j ACCEPT" 33 | #rule_append "FORWARD -s {{ calico_default_network }} -j ACCEPT" 34 | #{% if ansible_eth0 is defined %} 35 | #rule_append "INPUT -i eth0 -j ACCEPT" 36 | #{% endif %} 37 | 38 | nat_chain_add "lain-PREROUTING" 39 | nat_rule_append "PREROUTING -j lain-PREROUTING" 40 | nat_chain_add "lain-OUTPUT" 41 | nat_rule_append "OUTPUT -j lain-OUTPUT" 42 | -------------------------------------------------------------------------------- /playbooks/roles/images/meta/main.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: bootstrap-images 4 | - role: libraries 5 | -------------------------------------------------------------------------------- /playbooks/roles/images/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: pull images 2 | docker_pull_image: image={{ bootstrap_images[item] }} registry=registry.lain.local 3 | with_items: "{{ images }}" 4 | when: not bootstrapping|bool 5 | -------------------------------------------------------------------------------- /playbooks/roles/lainlet/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: libraries -------------------------------------------------------------------------------- /playbooks/roles/lainlet/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: set lainlet's auth configuration on etcd 2 | etcd_set_key: 3 | key: /lain/config/super_apps/{{item}} 4 | value: " {}" 5 | etcd_client_port: "{{ etcd_client_port }}" 6 | with_items: 7 | - console 8 | - webrouter 9 | - tinydns 10 | - backupctl 11 | - lvault 12 | - registry 13 | - hedwig 14 | - streamrouter 15 | 16 | - name: copy lainlet binary file 17 | copy: 18 | src: lainlet 19 | dest: /usr/bin/lainlet 20 | force: yes 21 | mode: a+x 22 | 23 | - name: generate lainlet.service 24 | template: 25 | src: lainlet.service.j2 26 | dest: /etc/systemd/system/lainlet.service 27 | 28 | - name: systemctl daemon-reload 29 | command: systemctl daemon-reload 30 | 31 | - name: systemctl restart lainlet && systemctl enable lainlet 32 | service: 33 | name: lainlet 34 | state: restarted 35 | enabled: yes 36 | 37 | - name: set lainlet restart cron 38 | template: src=lainlet.cron.j2 dest=/etc/cron.d/lainlet.cron -------------------------------------------------------------------------------- /playbooks/roles/lainlet/templates/lainlet.cron.j2: -------------------------------------------------------------------------------- 1 | 10 5 * * * root /bin/systemctl restart lainlet 2>&1 > /dev/null -------------------------------------------------------------------------------- /playbooks/roles/lainlet/templates/lainlet.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=lainlet 3 | After=network.target 4 | 5 | [Service] 6 | LimitNOFILE=65535 7 | ExecStart=/usr/bin/lainlet \ 8 | -web=:{{ lainlet_port }} \ 9 | -grpc.addr=:{{ lainlet_grpc_port }} \ 10 | -etcd={{ node_ip }}:{{ etcd_client_port }} \ 11 | -ip={{ node_ip }} \ 12 | -debug 13 | Restart=on-failure 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/docker_check_images.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from subprocess import Popen, PIPE 4 | 5 | from ansible.module_utils.basic import AnsibleModule 6 | 7 | 8 | def main(): 9 | module = AnsibleModule( 10 | argument_spec=dict( 11 | images=dict(type='list', required=True), 12 | ), 13 | ) 14 | 15 | images = module.params['images'] 16 | 17 | p = Popen(['docker', 'inspect', '-f', '{{.Id}}'] + images, stderr=PIPE) 18 | stderr = p.stderr.read() 19 | if p.wait() == 0: 20 | module.exit_json(changed=False) 21 | else: 22 | module.fail_json(msg=stderr) 23 | 24 | 25 | if __name__ == '__main__': 26 | main() 27 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/docker_inspect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import docker 4 | from docker.errors import APIError 5 | 6 | 7 | def main(): 8 | module = AnsibleModule( 9 | argument_spec=dict( 10 | name=dict(required=True), 11 | type=dict(default='container', choices=['container', 'image']), 12 | ), 13 | ) 14 | 15 | name = module.params['name'] 16 | type = module.params['type'] 17 | 18 | client = docker.Client() 19 | 20 | if type == 'container': 21 | inspect_method = client.inspect_container 22 | elif type == 'image': 23 | inspect_method = client.inspect_image 24 | else: 25 | # should not reach here 26 | raise Exception("unknown type") 27 | 28 | try: 29 | result = inspect_method(name) 30 | except APIError as e: 31 | if e.response.status_code == 404: 32 | module.fail_json(msg="%s does not exists" % name) 33 | raise 34 | 35 | result['changed'] = False 36 | module.exit_json(**result) 37 | 38 | 39 | from ansible.module_utils.basic import * 40 | main() 41 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/docker_pull_image.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from subprocess import call, check_call 4 | 5 | 6 | def main(): 7 | module = AnsibleModule( 8 | argument_spec=dict( 9 | image=dict(required=True), 10 | registry=dict(default=''), 11 | ), 12 | ) 13 | 14 | image = module.params['image'] 15 | registry = module.params['registry'] 16 | 17 | retval = call(['docker', 'inspect', '-f', '{{.Id}}', image]) 18 | if retval == 0: 19 | # image already exists 20 | module.exit_json(changed=False) 21 | 22 | if registry: 23 | src_image = '%s/%s' % (registry, image) 24 | check_call(['docker', 'pull', src_image]) 25 | check_call(['docker', 'tag', src_image, image]) 26 | else: 27 | check_call(['docker', 'pull', image]) 28 | 29 | module.exit_json(changed=True) 30 | 31 | from ansible.module_utils.basic import * 32 | 33 | main() 34 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/docker_push.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from subprocess import check_call 4 | from urllib2 import urlopen, HTTPError 5 | 6 | 7 | def main(): 8 | module = AnsibleModule( 9 | argument_spec=dict( 10 | image=dict(required=True), 11 | registry=dict(required=True), 12 | ), 13 | ) 14 | 15 | image = module.params['image'] 16 | registry = module.params['registry'] 17 | 18 | repo, tag = image.rsplit(':', 1) 19 | url = 'http://%s/v2/%s/manifests/%s' % (registry, repo, tag) 20 | try: 21 | urlopen(url) 22 | except HTTPError as e: 23 | if e.code != 404: 24 | raise 25 | else: 26 | # already in registry 27 | module.exit_json(changed=False) 28 | 29 | target = '%s/%s' % (registry, image) 30 | check_call(['docker', 'tag', image, target]) 31 | check_call(['docker', 'push', target]) 32 | module.exit_json(changed=True) 33 | 34 | 35 | from ansible.module_utils.basic import * 36 | main() 37 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/etcd_prepare_update.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | from subprocess import check_call 5 | 6 | from ansible.module_utils.basic import AnsibleModule 7 | 8 | 9 | def main(): 10 | module = AnsibleModule( 11 | argument_spec=dict( 12 | current_members=dict(type='list', required=True), 13 | wanted_members=dict(type='list', required=True), 14 | is_lain_manager=dict(type='bool', required=True), 15 | node_name=dict(type='str', required=True) 16 | ), 17 | ) 18 | 19 | current_members = module.params['current_members'] 20 | wanted_members = module.params['wanted_members'] 21 | is_lain_manager = module.params['is_lain_manager'] 22 | node_name = module.params['node_name'] 23 | 24 | if len(set(current_members).symmetric_difference(set(wanted_members))) > 1: 25 | module.fail_json(msg="you can only remove or add one node a time.") 26 | 27 | msg = 'skip etcd backup' 28 | if is_lain_manager: 29 | with open(os.devnull, 'w') as fnull: 30 | data_dir = '/var/etcd/%s' % node_name 31 | tmp_dir = '/tmp/lain/etcd_backup/' 32 | check_call(['rm', '-rf', tmp_dir], stdout=fnull) 33 | check_call(['etcdctl', 'backup', '--data-dir', data_dir, '--backup-dir', tmp_dir], stdout=fnull) 34 | msg = "backup etcd to %s successfully" % tmp_dir 35 | module.exit_json(changed=True, msg=msg) 36 | 37 | 38 | if __name__ == '__main__': 39 | main() 40 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/etcd_set_key.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from urllib2 import urlopen, HTTPError 4 | import json 5 | import os 6 | from subprocess import check_call 7 | 8 | from ansible.module_utils.basic import AnsibleModule 9 | 10 | 11 | def main(): 12 | module = AnsibleModule( 13 | argument_spec=dict( 14 | key=dict(type='str', required=True), 15 | value=dict(type='str', required=True), 16 | etcd_client_port=dict(type='str', default="4001"), 17 | ), 18 | ) 19 | 20 | key = module.params['key'] 21 | value = module.params['value'] 22 | endpoint = "http://127.0.0.1:%s" % module.params['etcd_client_port'] 23 | 24 | def get_key(key): 25 | try: 26 | f = urlopen('%s/v2/keys%s' % (endpoint, key)) 27 | except HTTPError as e: 28 | if e.code == 404: 29 | return None 30 | raise 31 | 32 | data = json.load(f) 33 | return data['node']['value'] 34 | 35 | def set_key(key, value): 36 | with open(os.devnull, 'w') as fnull: 37 | check_call(['etcdctl', 'set', key, value], stdout=fnull) 38 | 39 | current_value = get_key(key) 40 | 41 | if current_value == value: 42 | module.exit_json(changed=False) 43 | 44 | set_key(key, value) 45 | 46 | module.exit_json(changed=True) 47 | 48 | 49 | if __name__ == '__main__': 50 | main() 51 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/get_app_proc_ip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # get app virtual ip from networkd config (etcd) 4 | from subprocess import check_call, call, Popen, PIPE 5 | from ansible.module_utils.basic import * 6 | 7 | LAIN_VIP_PREFIX_KEY = "/lain/networkd/apps" 8 | 9 | module = AnsibleModule( 10 | argument_spec=dict( 11 | container_app=dict(required=True), 12 | container_proc=dict(required=True), 13 | ), 14 | ) 15 | 16 | 17 | def main(): 18 | container_app = module.params['container_app'] 19 | container_proc = module.params['container_proc'] 20 | 21 | changed = False 22 | config = get_proc_config(app=container_app, proc=container_proc) 23 | if not config: 24 | module.fail_json(msg="No available proc ip for %s %s" % (container_app, container_proc)) 25 | ip = config.get("ip") 26 | if not ip: 27 | module.fail_json(msg="No available proc ip for %s %s" % (container_app, container_proc)) 28 | port = config.get("port") 29 | if not port: 30 | module.fail_json(msg="No available proc ip for %s %s" % (container_app, container_proc)) 31 | module.exit_json(changed=changed, ip=ip, port=port) 32 | 33 | 34 | def get_proc_config(app, proc): 35 | prefix = "%s/%s/%s" % (LAIN_VIP_PREFIX_KEY, app, proc) 36 | keys = list_etcd_key(prefix) 37 | if not keys: 38 | module.fail_json(msg="No available virtual ip configs") 39 | data = {} 40 | for key in keys: 41 | pair = key[len(prefix)+1:] 42 | data["ip"], data["port"] = pair.split(':') 43 | return data 44 | 45 | 46 | def list_etcd_key(key): 47 | p = Popen(['etcdctl', 'ls', key], stdout=PIPE, stderr=PIPE) 48 | output, err = p.communicate() 49 | if p.returncode == 4: 50 | if "Key not found" in err: 51 | return [] 52 | else: 53 | module.fail_json(msg=err) 54 | elif p.returncode != 0: 55 | module.fail_json(msg=err) 56 | return output.rstrip().splitlines() 57 | 58 | 59 | def get_etcd_key(key): 60 | p = Popen(['etcdctl', 'get', key], stdout=PIPE, stderr=PIPE) 61 | output, err = p.communicate() 62 | if p.returncode == 4: 63 | if "Key not found" in err: 64 | return None 65 | else: 66 | module.fail_json(msg=err) 67 | elif p.returncode != 0: 68 | module.fail_json(msg=err) 69 | return output.rstrip() 70 | 71 | 72 | if __name__ == '__main__': 73 | main() 74 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/get_virtual_ip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # get app virtual ip from networkd config (etcd) 4 | from subprocess import check_call, call, Popen, PIPE 5 | from ansible.module_utils.basic import * 6 | 7 | LAIN_VIP_PREFIX_KEY = "/lain/config/vips" 8 | 9 | module = AnsibleModule( 10 | argument_spec=dict( 11 | container_app=dict(required=True), 12 | container_proc=dict(required=True), 13 | ), 14 | ) 15 | 16 | 17 | def main(): 18 | container_app = module.params['container_app'] 19 | container_proc = module.params['container_proc'] 20 | 21 | changed = False 22 | config = get_proc_config(app=container_app, proc=container_proc) 23 | if not config: 24 | module.fail_json(msg="No available virutual ip for %s %s" % (container_app, container_proc)) 25 | ip = config.get("ip") 26 | if not ip: 27 | module.fail_json(msg="No available virutual ip for %s %s" % (container_app, container_proc)) 28 | module.exit_json(changed=changed, ip=ip) 29 | 30 | 31 | def get_proc_config(app, proc): 32 | keys = list_etcd_key(LAIN_VIP_PREFIX_KEY) 33 | if not keys: 34 | module.fail_json(msg="No available virtual ip configs") 35 | for key in keys: 36 | value = get_etcd_key(key) 37 | if not value: 38 | return None 39 | data = json.loads(value) 40 | config_app = data.get("app") 41 | if config_app != app: 42 | continue 43 | config_proc = data.get("proc") 44 | if config_proc != proc: 45 | continue 46 | data["ip"] = key[len(LAIN_VIP_PREFIX_KEY)+1:] 47 | return data 48 | 49 | 50 | def list_etcd_key(key): 51 | p = Popen(['etcdctl', 'ls', key], stdout=PIPE, stderr=PIPE) 52 | output, err = p.communicate() 53 | if p.returncode == 4: 54 | if "Key not found" in err: 55 | return [] 56 | else: 57 | module.fail_json(msg=err) 58 | elif p.returncode != 0: 59 | module.fail_json(msg=err) 60 | return output.rstrip().splitlines() 61 | 62 | 63 | def get_etcd_key(key): 64 | p = Popen(['etcdctl', 'get', key], stdout=PIPE, stderr=PIPE) 65 | output, err = p.communicate() 66 | if p.returncode == 4: 67 | if "Key not found" in err: 68 | return None 69 | else: 70 | module.fail_json(msg=err) 71 | elif p.returncode != 0: 72 | module.fail_json(msg=err) 73 | return output.rstrip() 74 | 75 | 76 | if __name__ == '__main__': 77 | main() 78 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/post_json.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from urllib2 import Request, urlopen, HTTPError 4 | import json 5 | 6 | 7 | def main(): 8 | module = AnsibleModule( 9 | argument_spec=dict( 10 | url=dict(type='str', required=True), 11 | body=dict(type='dict', required=True), 12 | header=dict(type='dict', required=False), 13 | ) 14 | ) 15 | 16 | url = module.params['url'] 17 | body = module.params['body'] 18 | header = module.params['header'] 19 | 20 | req = Request(url) 21 | req.add_header('Content-Type', 'application/json') 22 | if header: 23 | for k, v in header.iteritems(): 24 | req.add_header(k, v) 25 | 26 | try: 27 | urlopen(req, json.dumps(body)) 28 | except HTTPError as e: 29 | module.fail_json(msg=e.reason, code=e.code, response=e.read()) 30 | else: 31 | module.exit_json(changed=True) 32 | 33 | from ansible.module_utils.basic import * 34 | main() 35 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/set_config_domain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from subprocess import Popen, PIPE 4 | from ansible.module_utils.basic import * 5 | 6 | LAIN_TINYDNS_PREFIX_KEY = "/lain/config/domains" 7 | 8 | module = AnsibleModule( 9 | argument_spec=dict( 10 | domain=dict(required=True), 11 | record=dict(required=True), 12 | ), 13 | ) 14 | 15 | 16 | def main(): 17 | 18 | domain = module.params['domain'] 19 | record = module.params['record'] 20 | changed = False 21 | 22 | old_config = get_config(domain) 23 | if not old_config: 24 | if record == "": 25 | module.exit_json(changed=changed) 26 | changed = True 27 | else: 28 | if len(old_config['ips']) > 1: 29 | changed = True 30 | elif old_config['ips'][0] != record: 31 | changed = True 32 | 33 | if changed is False: 34 | module.exit_json(changed=changed) 35 | 36 | set_config(domain, record) 37 | module.exit_json(changed=changed) 38 | 39 | 40 | def get_config(domain): 41 | key = "%s/%s" % (LAIN_TINYDNS_PREFIX_KEY, domain) 42 | value = get_etcd_key(key) 43 | 44 | if value is None: 45 | return None 46 | elif value == "": 47 | return None 48 | data = json.loads(value) 49 | return data 50 | 51 | 52 | def set_config(domain, record): 53 | key = "%s/%s" % (LAIN_TINYDNS_PREFIX_KEY, domain) 54 | if record == "": 55 | rm_etcd_key(key) 56 | return 57 | data = {"ips": [record]} 58 | value = json.dumps(data) 59 | prev_value = get_etcd_key(key) 60 | set_etcd_key(key, value, prev_value) 61 | 62 | 63 | def get_etcd_key(key): 64 | p = Popen(['etcdctl', 'get', key], stdout=PIPE, stderr=PIPE) 65 | output, err = p.communicate() 66 | if p.returncode == 4: 67 | if "Key not found" in err: 68 | return None 69 | else: 70 | module.fail_json(msg=err) 71 | elif p.returncode != 0: 72 | module.fail_json(msg=err) 73 | return output.rstrip() 74 | 75 | 76 | def set_etcd_key(key, value, prev_value=None): 77 | if prev_value is not None: 78 | cmd = ['etcdctl', 'set', key, value, '--swap-with-value', prev_value] 79 | else: 80 | cmd = ['etcdctl', 'set', key, value] 81 | p = Popen(cmd, stdout=PIPE, stderr=PIPE) 82 | output, err = p.communicate() 83 | if p.returncode != 0: 84 | module.fail_json(msg=err) 85 | 86 | 87 | def rm_etcd_key(key): 88 | cmd = ['etcdctl', 'rm', key] 89 | p = Popen(cmd, stdout=PIPE, stderr=PIPE) 90 | output, err = p.communicate() 91 | if p.returncode != 0: 92 | module.fail_json(msg=err) 93 | 94 | 95 | if __name__ == '__main__': 96 | main() 97 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/set_tinydns_domain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from subprocess import Popen, PIPE 4 | from ansible.module_utils.basic import * 5 | 6 | LAIN_TINYDNS_PREFIX_KEY = "/lain/config/tinydns_fqdns" 7 | 8 | module = AnsibleModule( 9 | argument_spec=dict( 10 | domain=dict(required=True), 11 | record=dict(required=True), 12 | ), 13 | ) 14 | 15 | 16 | def main(): 17 | 18 | domain = module.params['domain'] 19 | record = module.params['record'] 20 | changed = False 21 | 22 | old_config = get_config(domain) 23 | if not old_config: 24 | if record == "": 25 | module.exit_json(changed=changed) 26 | changed = True 27 | else: 28 | if len(old_config) > 1: 29 | changed = True 30 | elif old_config[0] != record: 31 | changed = True 32 | 33 | if changed is False: 34 | module.exit_json(changed=changed) 35 | 36 | set_config(domain, record) 37 | module.exit_json(changed=changed) 38 | 39 | 40 | def get_config(domain): 41 | key = "%s/%s" % (LAIN_TINYDNS_PREFIX_KEY, domain) 42 | value = get_etcd_key(key) 43 | 44 | if value is None: 45 | return None 46 | elif value == "": 47 | return None 48 | data = json.loads(value) 49 | return data 50 | 51 | 52 | def set_config(domain, record): 53 | key = "%s/%s" % (LAIN_TINYDNS_PREFIX_KEY, domain) 54 | if record == "": 55 | rm_etcd_key(key) 56 | return 57 | data = [record] 58 | value = json.dumps(data) 59 | prev_value = get_etcd_key(key) 60 | set_etcd_key(key, value, prev_value) 61 | 62 | 63 | def get_etcd_key(key): 64 | p = Popen(['etcdctl', 'get', key], stdout=PIPE, stderr=PIPE) 65 | output, err = p.communicate() 66 | if p.returncode == 4: 67 | if "Key not found" in err: 68 | return None 69 | else: 70 | module.fail_json(msg=err) 71 | elif p.returncode != 0: 72 | module.fail_json(msg=err) 73 | return output.rstrip() 74 | 75 | 76 | def set_etcd_key(key, value, prev_value=None): 77 | if prev_value is not None: 78 | cmd = ['etcdctl', 'set', key, value, '--swap-with-value', prev_value] 79 | else: 80 | cmd = ['etcdctl', 'set', key, value] 81 | p = Popen(cmd, stdout=PIPE, stderr=PIPE) 82 | output, err = p.communicate() 83 | if p.returncode != 0: 84 | module.fail_json(msg=err) 85 | 86 | 87 | def rm_etcd_key(key): 88 | cmd = ['etcdctl', 'rm', key] 89 | p = Popen(cmd, stdout=PIPE, stderr=PIPE) 90 | output, err = p.communicate() 91 | if p.returncode != 0: 92 | module.fail_json(msg=err) 93 | 94 | 95 | if __name__ == '__main__': 96 | main() 97 | -------------------------------------------------------------------------------- /playbooks/roles/libraries/library/set_virtual_ip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from subprocess import check_call, call, Popen, PIPE 4 | from ansible.module_utils.basic import * 5 | 6 | module = AnsibleModule( 7 | argument_spec=dict( 8 | ip=dict(required=True), 9 | device=dict(required=True), 10 | ), 11 | ) 12 | 13 | 14 | def main(): 15 | ip = module.params['ip'] 16 | device = module.params['device'] 17 | changed = set_vip(ip, device) 18 | module.exit_json(changed=changed) 19 | 20 | 21 | def set_vip(ip, device): 22 | if get_vip(ip, device): 23 | return False 24 | cmd = ['ip', 'addr', 'add', '%s/32' % ip, 'dev', device] 25 | p = Popen(cmd, stdout=PIPE, stderr=PIPE) 26 | output, err = p.communicate() 27 | if p.returncode != 0: 28 | module.fail_json(msg=err) 29 | return True 30 | 31 | 32 | def get_vip(ip, device): 33 | cmd = ['ip', 'addr', 'show', 'dev', device] 34 | p = Popen(cmd, stdout=PIPE, stderr=PIPE) 35 | output, err = p.communicate() 36 | if p.returncode != 0: 37 | module.fail_json(msg="ip addr show failed. stdout: %r" % output) 38 | if ' %s/32 ' % ip in output: 39 | return True 40 | return False 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /playbooks/roles/lvault-app/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | 3 | - role: console-deploy 4 | app: lvault 5 | -------------------------------------------------------------------------------- /playbooks/roles/manager/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - config 3 | -------------------------------------------------------------------------------- /playbooks/roles/manager/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: generate ssh key pair for cluster management 2 | command: ssh-keygen -t rsa -N "" -f /root/.ssh/lain 3 | args: 4 | creates: /root/.ssh/lain 5 | when: is_lain_manager 6 | 7 | - name: copy ssh key pair for cluster management 8 | copy: src=/root/.ssh/{{ item }} dest=/root/.ssh/{{ item }} 9 | with_items: 10 | - lain 11 | - lain.pub 12 | when: is_lain_manager 13 | 14 | - name: generate ssh finger print 15 | command: ssh-keyscan -t rsa {{ node_ip }} 16 | register: result 17 | changed_when: False 18 | delegate_to: 127.0.0.1 19 | 20 | - name: put the ssh finger print in known_hosts 21 | lineinfile: dest=/root/.ssh/known_hosts line='{{ result.stdout_lines | last }}' create=yes 22 | delegate_to: 127.0.0.1 23 | 24 | - name: authorize the management ssh key 25 | authorized_key: user=root key="{{ lookup('file', '/root/.ssh/lain.pub') }}" 26 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for moosefs-build 2 | command: systemctl daemon-reload 3 | 4 | - name: restart mfsmaster 5 | service: name=mfsmaster state=restarted 6 | 7 | - name: restart mfschunkserver 8 | service: name=mfschunkserver state=restarted 9 | 10 | - name: restart mfscgiserv 11 | service: name=mfscgiserv state=restarted 12 | 13 | - name: restart mfsmetalogger 14 | service: name=mfsmetalogger state=restarted 15 | 16 | - name: reload mfsmaster 17 | service: name=mfsmaster state=reloaded 18 | 19 | - name: reload mfschunkserver 20 | service: name=mfschunkserver state=reloaded 21 | 22 | - name: reload mfsmetalogger 23 | service: name=mfsmetalogger state=reloaded 24 | 25 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: binary 4 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/tasks/chunkserver.yaml: -------------------------------------------------------------------------------- 1 | - name: create mfs dir 2 | file: path={{ item }} state=directory group=mfs owner=mfs 3 | with_items: "{{ mfs_data_dir }}" 4 | 5 | - name: prepare mfshdd config file for mfschunkserver 6 | template: src=config/mfshdd.cfg.j2 dest=/etc/mfs/mfshdd.cfg 7 | notify: 8 | - reload mfschunkserver 9 | 10 | - name: generate configuration file for mfschunkserver 11 | template: src=config/mfschunkserver.cfg.j2 dest=/etc/mfs/mfschunkserver.cfg 12 | notify: 13 | - reload mfschunkserver 14 | 15 | - name: config mfschunkserver service 16 | template: src=service/mfschunkserver.service.j2 dest=/etc/systemd/system/mfschunkserver.service 17 | notify: 18 | - reload systemd for moosefs-build 19 | - restart mfschunkserver 20 | 21 | - meta: flush_handlers 22 | 23 | - name: ensure moosefs chunkserver started 24 | service: name=mfschunkserver enabled=yes state=started 25 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: extract moosefs rpm package 2 | command: tar --overwrite -z -x -f /tmp/lain/moosefs-3.0.47.tar.gz -C /tmp/ 3 | 4 | - name: install moosefs 5 | yum: name=/tmp/fuse-libs-2.9.2-5.el7.x86_64.rpm,/tmp/moosefs-cgi-3.0.47-1.rhsysv.x86_64.rpm,/tmp/moosefs-cgiserv-3.0.47-1.rhsysv.x86_64.rpm,/tmp/moosefs-chunkserver-3.0.47-1.rhsysv.x86_64.rpm,/tmp/moosefs-cli-3.0.47-1.rhsysv.x86_64.rpm,/tmp/moosefs-client-3.0.47-1.rhsysv.x86_64.rpm,/tmp/moosefs-master-3.0.47-1.rhsysv.x86_64.rpm,/tmp/moosefs-metalogger-3.0.47-1.rhsysv.x86_64.rpm,fuse 6 | 7 | - include: master.yaml 8 | when: is_moosefs_master 9 | 10 | - command: etcdctl get /lain/config/moosefs 11 | register: result 12 | failed_when: result.stdout == "" 13 | 14 | - name: write moosefs master addr into /etc/hosts 15 | lineinfile: dest=/etc/hosts line="{{ result.stdout.split(":")[0] }} mfsmaster" insertafter=EOF 16 | 17 | - include: chunkserver.yaml 18 | when: is_moosefs_chunkserver 19 | 20 | - include: metalogger.yaml 21 | when: is_moosefs_metalogger 22 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/tasks/master.yaml: -------------------------------------------------------------------------------- 1 | - name: gernerate configuration file for mfsmaster 2 | template: src=config/mfsmaster.cfg.j2 dest=/etc/mfs/mfsmaster.cfg 3 | notify: 4 | - reload mfsmaster 5 | 6 | - name: config mfsmaster service 7 | template: src=service/mfsmaster.service.j2 dest=/etc/systemd/system/mfsmaster.service 8 | notify: 9 | - reload systemd for moosefs-build 10 | - restart mfsmaster 11 | 12 | - name: config mfscgiserv service 13 | template: src=service/mfscgiserv.service.j2 dest=/etc/systemd/system/mfscgiserv.service 14 | notify: 15 | - reload systemd for moosefs-build 16 | - restart mfscgiserv 17 | 18 | - meta: flush_handlers 19 | 20 | - name: ensure moosefs master started 21 | service: name=mfsmaster enabled=yes state=started 22 | 23 | - name: ensure moosefs cgiserv started 24 | service: name=mfscgiserv enabled=yes state=started 25 | 26 | - name: write moosefs master's address into etcd 27 | command: etcdctl set /lain/config/moosefs "{{ node_ip }}:{{ mfsport }}" 28 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/tasks/metalogger.yaml: -------------------------------------------------------------------------------- 1 | - name: gernerate configuration file for mfsmetalogger 2 | template: src=config/mfsmetalogger.cfg.j2 dest=/etc/mfs/mfsmetalogger.cfg 3 | notify: 4 | - reload mfsmetalogger 5 | 6 | - name: config mfsmetalogger service 7 | template: src=service/mfsmetalogger.service.j2 dest=/etc/systemd/system/mfsmetalogger.service 8 | notify: 9 | - reload systemd for moosefs-build 10 | - restart mfsmetalogger 11 | 12 | - name: ensure moosefs metalogger started 13 | service: name=mfsmetalogger enabled=yes state=started 14 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/templates/config/mfshdd.cfg.j2: -------------------------------------------------------------------------------- 1 | # This file keeps definitions of mounting points (paths) of hard drives to use with chunk server. 2 | # A path may begin with extra '*', which means this hard drive is 'marked for removal' and all data will be replicated to other hard drives (usually on other chunkservers). 3 | # It is possible to specify optional space limit (after each mounting point), there are two ways of doing that: 4 | # - set space to be left unused on a hard drive (this overrides the default setting from mfschunkserver.cfg) 5 | # - limit space to be used on a hard drive 6 | # Space limit definition: [0-9]*(.[0-9]*)?([kMGTPE]|[KMGTPE]i)?B?, add minus in front for the first option. 7 | # 8 | # Examples: 9 | # 10 | # use hard drive '/mnt/hd1' with default options: 11 | #/mnt/hd1 12 | # 13 | # use hard drive '/mnt/hd2', but replicate all data from it: 14 | #*/mnt/hd2 15 | # 16 | # use hard drive '/mnt/hd3', but try to leave 5GiB on it: 17 | #/mnt/hd3 -5GiB 18 | # 19 | # use hard drive '/mnt/hd4', but use only 1.5TiB on it: 20 | #/mnt/hd4 1.5TiB 21 | {% for dir in mfs_data_dir %} 22 | {{ dir }} 23 | {% endfor %} 24 | 25 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/templates/config/mfsmetalogger.cfg.j2: -------------------------------------------------------------------------------- 1 | ############################################### 2 | # RUNTIME OPTIONS # 3 | ############################################### 4 | 5 | # user to run daemon as (default is mfs) 6 | # WORKING_USER = mfs 7 | 8 | # group to run daemon as (optional - if empty then default user group will be used) 9 | # WORKING_GROUP = mfs 10 | 11 | # name of process to place in syslog messages (default is mfsmetalogger) 12 | # SYSLOG_IDENT = mfsmetalogger 13 | 14 | # whether to perform mlockall() to avoid swapping out mfsmetalogger process (default is 0, i.e. no) 15 | # LOCK_MEMORY = 0 16 | 17 | # nice level to run daemon with (default is -19; note: process must be started as root to increase priority, if setting of priority fails, process retains the nice level it started with) 18 | # NICE_LEVEL = -19 19 | 20 | # set default umask for group and others (user has always 0, default is 027 - block write for group and block all for others) 21 | # FILE_UMASK = 027 22 | 23 | # where to store daemon lock file (default is /var/lib/mfs) 24 | # DATA_PATH = /var/lib/mfs 25 | 26 | # number of metadata change log files (default is 50) 27 | # BACK_LOGS = 50 28 | 29 | # number of previous metadata files to be kept (default is 3) 30 | # BACK_META_KEEP_PREVIOUS = 3 31 | 32 | # metadata download frequency in hours (default is 24, should be at least BACK_LOGS/2) 33 | # META_DOWNLOAD_FREQ = 24 34 | 35 | ############################################### 36 | # MASTER CONNECTION OPTIONS # 37 | ############################################### 38 | 39 | # delay in seconds before next try to reconnect to master if not connected (default is 5) 40 | # MASTER_RECONNECTION_DELAY = 5 41 | 42 | # local address to use for connecting with master (default is *, i.e. default local address) 43 | # BIND_HOST = * 44 | 45 | # MooseFS master host, IP is allowed only in single-master installations (default is mfsmaster) 46 | # MASTER_HOST = mfsmaster 47 | 48 | # MooseFS master supervisor port (default is 9419) 49 | # MASTER_PORT = 9419 50 | 51 | # timeout in seconds for master connections (default is 10) 52 | # MASTER_TIMEOUT = 10 53 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/templates/config/mfsmount.cfg.j2: -------------------------------------------------------------------------------- 1 | # The optional mfsmount.cfg file can be used to specify defaults for mfsmount. 2 | # Default mount options can be specified on one line separated by commas or 3 | # over several lines. 4 | # 5 | # Examples: 6 | # 7 | # nosuid,nodev 8 | # mfsmaster=mfsmaster 9 | # mfspassword=secret 10 | # 11 | # The default mount point can also be set. The default mount point must begin 12 | # with a "/" and be a fully qualified path. 13 | # 14 | # Example: 15 | # 16 | # /mnt/mfs 17 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/templates/service/mfscgiserv.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=mfscgiserv 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/sbin/mfscgiserv -f start 7 | ExecStop=/usr/sbin/mfscgiserv stop 8 | Restart=always 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/templates/service/mfschunkserver.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=mfschunkserver 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/sbin/mfschunkserver -f start 7 | ExecStop=/usr/sbin/mfschunkserver stop 8 | ExecReload=/usr/sbin/mfschunkserver -f reload 9 | Restart=on-failure 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/templates/service/mfsmaster.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=mfsmaster 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/sbin/mfsmaster -a -f start 7 | ExecStop=/usr/sbin/mfsmaster stop 8 | ExecReload=/usr/sbin/mfsmaster -f reload 9 | Restart=always 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs-build/templates/service/mfsmetalogger.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=mfsmetalogger 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/sbin/mfsmetalogger -f start 7 | ExecStop=/usr/sbin/mfsmetalogger stop 8 | ExecReload=/usr/sbin/mfsmetalogger -f reload 9 | Restart=on-failure 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: binary 3 | -------------------------------------------------------------------------------- /playbooks/roles/moosefs/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - fail: msg="moosefs master address not exist in /lain/config/moosefs" 2 | when: mfsmaster == "" 3 | 4 | 5 | - name: extract moosefs rpm package 6 | command: tar --overwrite -z -x -f /tmp/lain/moosefs-3.0.47.tar.gz -C /tmp/ 7 | 8 | - name: install moosefs 9 | yum: name=/tmp/fuse-libs-2.9.2-5.el7.x86_64.rpm,/tmp/moosefs-cli-3.0.47-1.rhsysv.x86_64.rpm,/tmp/moosefs-client-3.0.47-1.rhsysv.x86_64.rpm,fuse 10 | 11 | - name: write moosefs master addr into /etc/hosts 12 | lineinfile: dest=/etc/hosts line="{{ mfsmaster }} mfsmaster" insertafter=EOF 13 | 14 | - name: create /mfs 15 | file: path=/mfs state=directory 16 | 17 | - name: check if /mfs mounted 18 | command: mfsdirinfo /mfs 19 | register: result 20 | ignore_errors: yes 21 | 22 | - name: set /etc/fstab, automount moosefs 23 | lineinfile: dest=/etc/fstab line="mfsmount /mfs fuse _netdev 0 0" insertafter=EOF 24 | 25 | # mfsmount may failed. it return error "EAGAIN (Resource temporarily unavailable)", means we need to try again later 26 | # try 5 time every 5 seconds. 27 | - name: mount /mfs -P "{{ mfsport }}" 28 | command: mount /mfs 29 | register: mount_result 30 | until: mount_result|success 31 | retries: 50 32 | delay: 5 33 | when: result|failed 34 | -------------------------------------------------------------------------------- /playbooks/roles/mysql/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: images 4 | images: 5 | - mysql -------------------------------------------------------------------------------- /playbooks/roles/mysql/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: run container 2 | command: | 3 | docker run -d 4 | --name mysql_container 5 | --net=host 6 | -e MYSQL_ALLOW_EMPTY_PASSWORD=yes 7 | -v {{ mysql_data_dir }}:/var/lib/mysql 8 | {{ mysql_image }} 9 | 10 | - name: wait to make sure MySQL being started 11 | wait_for: port=3306 state=started timeout=15 12 | 13 | - name: initialize databases 14 | command: | 15 | docker exec mysql_container mysql -uroot -e " 16 | CREATE DATABASE IF NOT EXISTS console; 17 | GRANT ALL ON console.* TO console@'%' IDENTIFIED BY 'console'; 18 | FLUSH PRIVILEGES; 19 | " 20 | -------------------------------------------------------------------------------- /playbooks/roles/network-recover/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: libraries -------------------------------------------------------------------------------- /playbooks/roles/network-recover/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: inspect docker network 2 | inspect_docker_network: 3 | node: "{{ recover_node }}" 4 | app: "{{ recover_app }}" 5 | proc: "{{ recover_proc }}" 6 | instance_number: "{{ recover_instance_number }}" 7 | client_app: "{{ recover_client_app }}" 8 | register: result 9 | 10 | - set_fact: network_id="{{ result.network_id }}" 11 | when: result|success 12 | 13 | - set_fact: endpoint_id="{{ result.endpoint_id }}" 14 | when: result|success 15 | 16 | - set_fact: recycle_ip="{{ result.recycle_ip }}" 17 | when: result|success 18 | 19 | - name: remove dirty endpoint in docker directory of etcd 20 | shell: etcdctl rm /docker/network/v1.0/endpoint/{{ network_id }}/{{ endpoint_id }} 21 | ignore_errors: yes 22 | when: is_lain_manager and result|success 23 | 24 | - name: remove dirty endpoint in calico directory of etcd 25 | shell: etcdctl rm /calico/v1/host/{{ recover_node }}/workload/libnetwork/libnetwork/endpoint/{{ endpoint_id }} 26 | ignore_errors: yes 27 | when: is_lain_manager and result|success 28 | 29 | - name: recycle the used ip 30 | shell: calicoctl ipam release --ip {{ recycle_ip }} 31 | when: is_lain_manager and result|success 32 | -------------------------------------------------------------------------------- /playbooks/roles/networkd-preupgrade/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | # ansible bug: get_url fails to retrieve https url through http proxy, so just use command 2 | - name: get networkd:v2.4.0 3 | command: wget https://lain.oss-cn-beijing.aliyuncs.com/binary/networkd/releases/download/v2.4.0/networkd.xz -O /tmp/networkd.xz 4 | 5 | - name: unxz networkd.xz 6 | command: unxz -kf /tmp/networkd.xz 7 | 8 | - name: set etcd domains from /etc/dnsmasq.conf 9 | command: etcdctl set /lain/config/domains/{{ item.split('/')[-2] }} '{"ips":[],"type":"node"}' 10 | with_lines: cat /etc/dnsmasq.conf 11 | when: item | regex_search('(^address=)') and not item | regex_search('(^address=/lain.local)') 12 | 13 | - name: set etcd domain for *.lain.local 14 | command: etcdctl set /lain/config/domains/*.lain.local '{"ips":[],"type":"webrouter"}' 15 | 16 | - name: load dnshijack keys from etcd 17 | command: etcdctl ls /lain/config/dnshijack 18 | register: hijack_domain_list 19 | ignore_errors: yes 20 | changed_when: False 21 | 22 | - name : load dnshijack settings from etcd 23 | command: etcdctl get {{ item.1 }} 24 | register: hijack_ip_list 25 | with_indexed_items: "{{ hijack_domain_list.stdout_lines }}" 26 | when: hijack_domain_list|success and hijack_domain_list.stdout != "" 27 | changed_when: False 28 | 29 | - name: set etcd domains from dnshijack 30 | command: etcdctl set /lain/config/domains/*.{{ item.1.split('/')[-1] }} '{"ips":["{{ hijack_ip_list.results[item.0].stdout }}"],"type":""}' 31 | with_indexed_items: "{{ hijack_domain_list.stdout_lines }}" 32 | when: hijack_domain_list|success and hijack_domain_list.stdout != "" 33 | 34 | - name: load dnsmasq_addresses keys from etcd 35 | command: etcdctl ls /lain/config/dnsmasq_addresses 36 | register: dnsmasq_domain_list 37 | ignore_errors: yes 38 | changed_when: False 39 | 40 | - name: load dnsmasq_addresses settings from etcd 41 | command: etcdctl get {{ item }} 42 | register: dnsmasq_ip_list 43 | with_items: " {{ dnsmasq_domain_list.stdout_lines }} " 44 | when: dnsmasq_domain_list|success and dnsmasq_domain_list.stdout != "" 45 | changed_when: False 46 | 47 | - name: set etcd domains from dnsmasq_addresses 48 | command: etcdctl set /lain/config/domains/{{ item.1.split('/')[-1] }} '{{ dnsmasq_ip_list.results[item.0].stdout }}' 49 | with_indexed_items: "{{ dnsmasq_domain_list.stdout_lines }}" 50 | when: dnsmasq_domain_list|success and dnsmasq_domain_list.stdout != "" 51 | -------------------------------------------------------------------------------- /playbooks/roles/networkd-rollback/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: rollback networkd 2 | copy: 3 | remote_src: True 4 | src: /usr/bin/networkd.back 5 | dest: /usr/bin/networkd 6 | force: yes 7 | 8 | - name: rollback networkd.service 9 | copy: 10 | remote_src: True 11 | src: /etc/systemd/system/networkd.service.back 12 | dest: /etc/systemd/system/networkd.service 13 | force: yes 14 | 15 | - name: systemctl daemon-reload 16 | command: systemctl daemon-reload 17 | 18 | - name: systemctl restart networkd && systemctl enable networkd 19 | service: 20 | name: networkd 21 | state: restarted 22 | enabled: yes 23 | 24 | - name: systemctl restart dnsmasq && systemctl enable dnsmasq 25 | service: 26 | name: dnsmasq 27 | state: started 28 | enabled: yes 29 | 30 | -------------------------------------------------------------------------------- /playbooks/roles/networkd-update/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | # this role is used to update networkd binary, the updated networkd binary should be located at dir: ../files 2 | - name: copy networkd binary file 3 | copy: 4 | src: networkd 5 | dest: /usr/bin/networkd 6 | force: yes 7 | mode: a+x 8 | 9 | - name: systemctl restart networkd && systemctl enable networkd 10 | service: 11 | name: networkd 12 | state: restarted 13 | enabled: yes 14 | 15 | -------------------------------------------------------------------------------- /playbooks/roles/networkd-upgrade/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config -------------------------------------------------------------------------------- /playbooks/roles/networkd-upgrade/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: backup original networkd 2 | copy: 3 | remote_src: True 4 | src: /usr/bin/networkd 5 | dest: /usr/bin/networkd.back 6 | force: yes 7 | 8 | - name: backup original networkd.service 9 | copy: 10 | remote_src: True 11 | src: /etc/systemd/system/networkd.service 12 | dest: /etc/systemd/system/networkd.service.back 13 | force: yes 14 | 15 | - name: copy networkd binary file 16 | copy: 17 | src: /tmp/networkd 18 | dest: /usr/bin/networkd 19 | force: yes 20 | mode: a+x 21 | 22 | - name: install arping 23 | # package name is iputils-arping not arping 24 | package: name=iputils-arping state=present 25 | when: ansible_distribution=="Ubuntu" 26 | 27 | - name: generate networkd.service 28 | template: 29 | src: networkd.service.j2 30 | dest: /etc/systemd/system/networkd.service 31 | 32 | - name: systemctl stop dnsmasq && systemctl disable dnsmasq 33 | service: 34 | name: dnsmasq 35 | state: stopped 36 | enabled: no 37 | 38 | - name: systemctl daemon-reload 39 | command: systemctl daemon-reload 40 | 41 | - name: systemctl restart networkd && systemctl enable networkd 42 | service: 43 | name: networkd 44 | state: restarted 45 | enabled: yes 46 | -------------------------------------------------------------------------------- /playbooks/roles/networkd-upgrade/templates/networkd.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=networkd 3 | After=network.target lainlet.service 4 | 5 | [Service] 6 | Environment=ETCD_AUTHORITY=127.0.0.1:{{ etcd_client_port }} 7 | LimitNOFILE=65535 8 | ExecStart=/usr/bin/networkd {% if domain == 'lain.local' and vip == '0.0.0.0' %}--domain=lain.local{% endif %} \ 9 | --net.interface={{ net_interface }} \ 10 | --lainlet.endpoint={{ node_ip }}:{{ lainlet_port }} \ 11 | --etcd.endpoint=http://{{ node_ip }}:{{ etcd_client_port }} \ 12 | --libnetwork \ 13 | --tinydns \ 14 | --swarm \ 15 | --deployd \ 16 | --webrouter \ 17 | --streamrouter \ 18 | --godns.addr=0.0.0.0:53 19 | 20 | Restart=on-failure 21 | 22 | [Install] 23 | WantedBy=multi-user.target 24 | -------------------------------------------------------------------------------- /playbooks/roles/networkd/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: lainlet 4 | -------------------------------------------------------------------------------- /playbooks/roles/networkd/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: copy networkd binary file 2 | copy: 3 | src: networkd 4 | dest: /usr/bin/networkd 5 | force: yes 6 | mode: a+x 7 | 8 | - name: install arping 9 | # package name is iputils-arping not arping 10 | package: name=iputils-arping state=present 11 | when: ansible_distribution=="Ubuntu" 12 | 13 | - name: generate networkd.service 14 | template: 15 | src: networkd.service.j2 16 | dest: /etc/systemd/system/networkd.service 17 | 18 | - name: systemctl daemon-reload 19 | command: systemctl daemon-reload 20 | 21 | - name: systemctl restart networkd && systemctl enable networkd 22 | service: 23 | name: networkd 24 | state: restarted 25 | enabled: yes 26 | -------------------------------------------------------------------------------- /playbooks/roles/networkd/templates/networkd.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=networkd 3 | After=network.target lainlet.service 4 | 5 | [Service] 6 | Environment=ETCD_AUTHORITY=127.0.0.1:{{ etcd_client_port }} 7 | LimitNOFILE=65535 8 | ExecStart=/usr/bin/networkd {% if domain == 'lain.local' and vip == '0.0.0.0' %}--domain=lain.local{% endif %} \ 9 | --net.interface={{ net_interface }} \ 10 | --lainlet.endpoint={{ node_ip }}:{{ lainlet_port }} \ 11 | --etcd.endpoint=http://{{ node_ip }}:{{ etcd_client_port }} \ 12 | --libnetwork \ 13 | --tinydns \ 14 | --swarm \ 15 | --deployd \ 16 | --webrouter \ 17 | --streamrouter \ 18 | --godns.addr=0.0.0.0:53 19 | 20 | Restart=on-failure 21 | 22 | [Install] 23 | WantedBy=multi-user.target 24 | -------------------------------------------------------------------------------- /playbooks/roles/node-change-labels/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: read current /etc/docker/daemon.json 2 | command: cat /etc/docker/daemon.json 3 | register: result 4 | 5 | - name: set old_docker_daemon_json 6 | set_fact: 7 | old_docker_daemon_json: '{{ result.stdout | from_json }}' 8 | 9 | - name: set old_labels 10 | set_fact: 11 | old_labels: '{{ old_docker_daemon_json.labels | default([]) }}' 12 | 13 | - name: merge diff_labels to old_labels when change_type == add 14 | set_fact: 15 | new_labels: '{{ old_labels | union(diff_labels) }}' 16 | when: change_type == "add" 17 | 18 | - name: delete diff_labels from old_labels when change_type == delete 19 | set_fact: 20 | new_labels: '{{ old_labels | difference(diff_labels) }}' 21 | when: change_type == "delete" 22 | 23 | - name: set new_docker_daemon_json 24 | set_fact: 25 | new_docker_daemon_json: '{{ old_docker_daemon_json | combine({"labels": new_labels}) | to_nice_json }}' 26 | 27 | - name: generate new /etc/docker/daemon.json 28 | template: 29 | src: docker-daemon.json.j2 30 | dest: /etc/docker/daemon.json 31 | owner: root 32 | group: root 33 | mode: 0644 34 | 35 | - name: reload docker 36 | service: 37 | name: docker 38 | state: reloaded 39 | -------------------------------------------------------------------------------- /playbooks/roles/node-change-labels/templates/docker-daemon.json.j2: -------------------------------------------------------------------------------- 1 | {{ new_docker_daemon_json }} 2 | -------------------------------------------------------------------------------- /playbooks/roles/node-clean/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: libraries 3 | -------------------------------------------------------------------------------- /playbooks/roles/node-clean/tasks/image-clean.yaml: -------------------------------------------------------------------------------- 1 | - name: collect running images 2 | shell: docker ps --format \{\{.Image\}\} | awk '{split($1, array, ":"); print array[1]}' 3 | register: images 4 | changed_when: False 5 | 6 | - name: display to-save images of each node 7 | debug: var=images.stdout_lines 8 | 9 | - name: "check finding the correct images to save. Next: clean images" 10 | pause: seconds=600 11 | 12 | - name: remove extra images 13 | image_clean: 14 | images: "{{ images.stdout_lines }}" 15 | redundancy: 3 16 | -------------------------------------------------------------------------------- /playbooks/roles/node-clean/tasks/log-clean.yaml: -------------------------------------------------------------------------------- 1 | - name: collect log files need to clean in /var/log 2 | shell: ls -1 /var/log/messages-* 3 | register: log_files 4 | changed_when: False 5 | ignore_errors: yes 6 | 7 | - name: display the log files need to clean of each node 8 | debug: var=log_files.stdout_lines 9 | when: log_files|success 10 | 11 | - name: "check finding the correct log files to clean. Next: clean log files" 12 | pause: seconds=600 13 | when: log_files|success 14 | 15 | - name: clean log files in /var/log 16 | command: /usr/bin/rm -f {{ item }} 17 | with_items: "{{ log_files.stdout_lines }}" 18 | when: log_files|success and log_files.stdout != "" 19 | -------------------------------------------------------------------------------- /playbooks/roles/node-clean/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - include: log-clean.yaml 2 | 3 | - include: image-clean.yaml 4 | -------------------------------------------------------------------------------- /playbooks/roles/node-disable-log-driver/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: read current /etc/docker/daemon.json 2 | command: cat /etc/docker/daemon.json 3 | register: result 4 | 5 | - name: set old_docker_daemon_json 6 | set_fact: 7 | old_docker_daemon_json: '{{ result.stdout | from_json }}' 8 | new_docker_daemon_json: {} 9 | 10 | - name: initialize new_docker_daemon_json without `log-opts` 11 | set_fact: 12 | new_docker_daemon_json: '{{ new_docker_daemon_json | combine({item.key: item.value}) }}' 13 | when: item.key != "log-opts" 14 | with_dict: '{{ old_docker_daemon_json }}' 15 | 16 | - name: set `log-driver` to none in new_docker_daemon_json 17 | set_fact: 18 | new_docker_daemon_json: '{{ new_docker_daemon_json | combine({"log-driver": "none"}) | to_nice_json }}' 19 | 20 | - name: generate new /etc/docker/daemon.json 21 | template: 22 | src: docker-daemon.json.j2 23 | dest: /etc/docker/daemon.json 24 | owner: root 25 | group: root 26 | mode: 0644 27 | 28 | - name: restart docker 29 | service: 30 | name: docker 31 | state: restarted 32 | -------------------------------------------------------------------------------- /playbooks/roles/node-disable-log-driver/templates/docker-daemon.json.j2: -------------------------------------------------------------------------------- 1 | {{ new_docker_daemon_json }} 2 | -------------------------------------------------------------------------------- /playbooks/roles/node/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: libraries 4 | - role: etcd 5 | - role: consul 6 | - role: prepare 7 | - role: firewall 8 | - role: binary 9 | - role: manager 10 | - role: docker 11 | - role: calico 12 | - role: ssl 13 | - role: lainlet 14 | - role: rebellion 15 | - role: networkd 16 | - role: moosefs 17 | when: mfsmaster != "" 18 | - role: backupd 19 | when: backup_enabled is defined and backup_enabled == "True" 20 | - role: registry 21 | action: "usemfs" 22 | when: registry_on_moosefs is defined and registry_on_moosefs == "True" 23 | - role: rsync 24 | - role: swarm 25 | -------------------------------------------------------------------------------- /playbooks/roles/os-dependent-vars/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: Load a variable file based on the OS type 2 | include_vars: "{{ item }}" 3 | with_first_found: 4 | - "../vars/{{ ansible_distribution }}-{{ ansible_distribution_major_version | int}}.yml" 5 | - "../vars/{{ ansible_distribution }}.yml" 6 | - "../vars/{{ ansible_os_family }}.yml" 7 | - "../vars/default.yml" 8 | -------------------------------------------------------------------------------- /playbooks/roles/os-dependent-vars/vars/CentOS.yml: -------------------------------------------------------------------------------- 1 | rsync_service_name: rsyncd 2 | rsync_service_path: /usr/lib/systemd/system/rsyncd.service 3 | 4 | conntrack_pkt_name: conntrack-tools 5 | 6 | iptables_pkt_name: iptables-services 7 | iptables_save_path: /etc/sysconfig/iptables 8 | 9 | docker_service_path: /usr/lib/systemd/system/docker.service 10 | 11 | collectd_conf_path: /etc/collectd.conf 12 | collectd_conf_dir: /etc/collectd.d 13 | -------------------------------------------------------------------------------- /playbooks/roles/os-dependent-vars/vars/Ubuntu.yml: -------------------------------------------------------------------------------- 1 | rsync_service_name: rsync 2 | rsync_service_path: /lib/systemd/system/rsync.service 3 | 4 | conntrack_pkt_name: conntrack 5 | 6 | iptables_pkt_name: iptables-persistent 7 | iptables_save_path: /etc/iptables/iptables 8 | 9 | docker_service_path: /lib/systemd/system/docker.service 10 | 11 | collectd_conf_path: /etc/collectd/collectd.conf 12 | collectd_conf_dir: /etc/collectd/collectd.conf.d 13 | -------------------------------------------------------------------------------- /playbooks/roles/packages/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | -------------------------------------------------------------------------------- /playbooks/roles/packages/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: install pip 2 | package: name=python-pip state=present 3 | 4 | - name: install python packages 5 | command: pip install --upgrade --force-reinstall {{ item }} 6 | with_items: 7 | - pip 8 | - python-dateutil==2.5.2 9 | - python-etcd==0.4.3 10 | - docker-py==1.8.0 11 | - psutil==4.1.0 12 | - requests==2.11.1 13 | register: result 14 | changed_when: "'Successfully installed' in result.stdout" 15 | 16 | - name: install lainctl 17 | command: pip install --upgrade --force-reinstall lain-admin-cli==v2.1.2 18 | when: bootstrapping is defined and bootstrapping|bool 19 | -------------------------------------------------------------------------------- /playbooks/roles/prepare/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | -------------------------------------------------------------------------------- /playbooks/roles/prepare/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: ensure epel exists 2 | yum: pkg=epel-release 3 | when: ansible_distribution == "CentOS" 4 | 5 | - name: create data dir 6 | file: path={{ lain_data_dir }} state=directory 7 | 8 | - yum: pkg=libselinux-python 9 | when: ansible_distribution == "CentOS" 10 | 11 | - name: enable selinux 12 | selinux: policy=targeted state=permissive 13 | when: ansible_distribution == "CentOS" 14 | 15 | - name: get stat of nscd.service 16 | stat: path=/etc/systemd/system/nscd.service 17 | register: nscd 18 | ignore_errors: yes 19 | 20 | - name: disable nscd 21 | service: name=nscd enabled=no state=stopped 22 | when: nscd.stat.exists 23 | 24 | - name: load node info from etcd 25 | command: etcdctl get /lain/nodes/nodes/{{ node_name }}:{{ node_ip }}:{{ ssh_port }} 26 | register: result 27 | ignore_errors: yes 28 | 29 | - name: config default domains 30 | command: etcdctl set /lain/config/domains/{{ item }} '{"ips":[],"type":"node"}' 31 | with_items: 32 | - etcd.lain 33 | - consul.lain 34 | - docker.lain 35 | - lainlet.lain 36 | - metric.lain 37 | 38 | - name: config *.lain.local resolved to webrouter vips 39 | command: etcdctl set /lain/config/domains/*.lain.local '{"ips":[],"type":"webrouter"}' 40 | 41 | - name: delete any interface calico created previously 42 | command: for interface in $(ip link show | grep cali | awk '{print $2}' | awk -F':' '{print $1}'); do ip link delete ${interface}; done 43 | ignore_errors: yes 44 | 45 | - set_fact: 46 | node_info: "{{result.stdout|from_json}}" 47 | when: result|success and result.stdout != "" 48 | ignore_errors: yes 49 | -------------------------------------------------------------------------------- /playbooks/roles/rebellion-upgrade-2.0.x-to-v2.3.0/files/clean_logs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import requests 4 | import json 5 | 6 | def clean_content(file_path): 7 | print "Cleaning %s..." % file_path 8 | open(file_path, 'w').close() 9 | 10 | def clean_webrouter(): 11 | webrouter_dir1 = '/data/lain/volumes/webrouter/webrouter.worker.worker/1/var/log/nginx' 12 | webrouter_dir2 = '/data/lain/volumes/webrouter/webrouter.worker.worker/2/var/log/nginx' 13 | for root, dirs, files in os.walk(webrouter_dir1, True): 14 | for name in files: 15 | clean_content(os.path.join(root, name)) 16 | for root, dirs, files in os.walk(webrouter_dir2, True): 17 | for name in files: 18 | clean_content(os.path.join(root, name)) 19 | 20 | def clean_syslog(): 21 | clean_content("/var/log/messages") 22 | 23 | def clean_applog(): 24 | resp = requests.get('http://lainlet.lain:9001/v2/rebellion/localprocs') 25 | respData = resp.json() 26 | for proc_name, proc_info in respData.iteritems(): 27 | parts = proc_name.split(".") 28 | if len(parts) < 3: 29 | continue 30 | app_name = parts[0] 31 | if len(proc_info["PodInfos"]) > 0: 32 | container_info = proc_info["PodInfos"][0] 33 | annotations = json.loads(container_info["Annotation"]) 34 | if "logs" in annotations: 35 | for log_file in annotations["logs"]: 36 | clean_content(os.path.join("/data/lain/volumes", app_name, proc_name, str(container_info["InstanceNo"]), "lain/logs", log_file)) 37 | 38 | if __name__ == "__main__": 39 | clean_webrouter() 40 | clean_syslog() 41 | clean_applog() -------------------------------------------------------------------------------- /playbooks/roles/rebellion-upgrade-2.0.x-to-v2.3.0/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for rebellion 2 | command: systemctl daemon-reload -------------------------------------------------------------------------------- /playbooks/roles/rebellion-upgrade-2.0.x-to-v2.3.0/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | -------------------------------------------------------------------------------- /playbooks/roles/rebellion-upgrade-2.0.x-to-v2.3.0/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | # !Caution: this task only applies for rebellion 2.0.x -> v2.3.0 2 | - name: pull new rebellion image 3 | command: docker pull registry.lain.local/rebellion:v2.3.0 4 | 5 | - name: tag new rebellion image 6 | command: docker tag registry.lain.local/rebellion:v2.3.0 rebellion:v2.3.0 7 | 8 | - name: prepare clean log script 9 | copy: src=clean_logs.py dest=/tmp/clean_logs.py mode="u+x" 10 | 11 | - name: update rebellion.service configuration 12 | template: src=rebellion.service.j2 dest=/etc/systemd/system/rebellion.service 13 | notify: 14 | - reload systemd for rebellion 15 | - meta: flush_handlers 16 | 17 | - name: clean log files 18 | command: python /tmp/clean_logs.py 19 | 20 | - name: restart rebellion 21 | service: name=rebellion enabled=yes state=restarted -------------------------------------------------------------------------------- /playbooks/roles/rebellion-upgrade-2.0.x-to-v2.3.0/templates/rebellion.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=rebellion 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | ExecStart=/usr/bin/docker run \ 8 | --name %n --rm --network=host \ 9 | -e LAINLET_PORT={{ lainlet_port }} \ 10 | -v {{ lain_data_dir }}/volumes/:/data/lain/volumes/:ro \ 11 | -v {{ lain_data_dir }}/rebellion/var/lib/filebeat/:/var/lib/filebeat \ 12 | -v {{ lain_data_dir }}/rebellion/logs/filebeat:/var/log/filebeat/ \ 13 | -v {{ lain_data_dir }}/rebellion/logs/supervisor:/var/log/supervisor/ \ 14 | -v /var/log/:/var/log/syslog/:ro \ 15 | rebellion:v2.3.0 16 | ExecStop=/usr/bin/docker stop %n 17 | Restart=always 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /playbooks/roles/rebellion-upgrade/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for rebellion 2 | command: systemctl daemon-reload -------------------------------------------------------------------------------- /playbooks/roles/rebellion-upgrade/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | -------------------------------------------------------------------------------- /playbooks/roles/rebellion-upgrade/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | # !Caution: this task only applies for rebellion v2.3.0 -> higher 2 | - name: pull new rebellion image 3 | command: docker pull registry.lain.local/rebellion:{{ upgrade_version }} 4 | 5 | - name: tag new rebellion image 6 | command: docker tag registry.lain.local/rebellion:{{ upgrade_version }} rebellion:{{ upgrade_version }} 7 | 8 | - name: update rebellion.service configuration 9 | template: src=rebellion.service.j2 dest=/etc/systemd/system/rebellion.service 10 | notify: 11 | - reload systemd for rebellion 12 | - meta: flush_handlers 13 | 14 | - name: restart rebellion 15 | service: name=rebellion enabled=yes state=restarted -------------------------------------------------------------------------------- /playbooks/roles/rebellion-upgrade/templates/rebellion.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=rebellion 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | ExecStart=/usr/bin/docker run \ 8 | --name %n --rm --network=host \ 9 | -e LAINLET_PORT={{ lainlet_port }} \ 10 | -v {{ lain_data_dir }}/volumes/:/data/lain/volumes/:ro \ 11 | -v {{ lain_data_dir }}/rebellion/var/lib/filebeat/:/var/lib/filebeat \ 12 | -v {{ lain_data_dir }}/rebellion/logs/filebeat:/var/log/filebeat/ \ 13 | -v {{ lain_data_dir }}/rebellion/logs/supervisor:/var/log/supervisor/ \ 14 | -v /var/log/:/var/log/syslog/:ro \ 15 | rebellion:{{ upgrade_version }} 16 | ExecStop=/usr/bin/docker stop %n 17 | Restart=always 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /playbooks/roles/rebellion/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for rebellion 2 | command: systemctl daemon-reload 3 | 4 | - name: restart rebellion 5 | service: name=rebellion state=restarted 6 | 7 | - name: restart rsyslog 8 | service: name=rsyslog state=restarted -------------------------------------------------------------------------------- /playbooks/roles/rebellion/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: images 4 | images: 5 | - rebellion 6 | -------------------------------------------------------------------------------- /playbooks/roles/rebellion/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: install rsyslog 2 | package: name=rsyslog state=present 3 | 4 | - name: config rebellion 5 | template: src=rebellion.service.j2 dest=/etc/systemd/system/rebellion.service 6 | notify: 7 | - reload systemd for rebellion 8 | - restart rebellion 9 | - meta: flush_handlers 10 | 11 | - name: ensure rebellion started 12 | service: name=rebellion enabled=yes state=started 13 | 14 | - name: checking rebellion working correctly 15 | shell: "docker ps | grep rebellion.service" 16 | register: result 17 | until: result|success 18 | retries: 50 19 | delay: 5 20 | changed_when: False 21 | 22 | - name: deploy rsyslog config 23 | template: src=10-docker-rsyslog.conf.j2 dest=/etc/rsyslog.d/10-docker-rsyslog.conf 24 | notify: 25 | - restart rsyslog 26 | -------------------------------------------------------------------------------- /playbooks/roles/rebellion/templates/10-docker-rsyslog.conf.j2: -------------------------------------------------------------------------------- 1 | :programname, startswith, "docker" { 2 | $template tpllogd,"<%pri%> %timestamp:::date-rfc3339% %HOSTNAME% %syslogtag% %msg%\n" 3 | 4 | # ### begin forwarding rule ### 5 | # The statement between the begin ... end define a SINGLE forwarding 6 | # rule. They belong together, do NOT split them. If you create multiple 7 | # forwarding rules, duplicate the whole block! 8 | # Remote Logging (we use TCP for reliable delivery) 9 | # 10 | # An on-disk queue is created for this action. If the remote host is 11 | # down, messages are spooled to disk and sent when it is up again. 12 | $ActionQueueFileName fwdRule1 # unique name prefix for spool files 13 | $ActionQueueMaxDiskSpace 1g # 1gb space limit (use as much as possible) 14 | $ActionQueueSaveOnShutdown on # save messages to disk on shutdown 15 | $ActionQueueType LinkedList # run asynchronously 16 | $ActionResumeRetryCount -1 # infinite retries if host is down 17 | *.* @@{{ node_ip }}:{{ rebellion_rsyslog_tcp_port }};tpllogd 18 | # ### end of the forwarding rule ### 19 | } -------------------------------------------------------------------------------- /playbooks/roles/rebellion/templates/rebellion.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=rebellion 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | ExecStartPre=-/usr/bin/docker stop %n 8 | ExecStartPre=-/usr/bin/docker rm %n 9 | ExecStart=/usr/bin/docker run \ 10 | --name %n --restart=always --network=host \ 11 | -e LAINLET_PORT={{ lainlet_port }} \ 12 | -v {{ lain_data_dir }}/volumes/:/data/lain/volumes/:ro \ 13 | -v {{ lain_data_dir }}/rebellion/var/lib/filebeat/:/var/lib/filebeat \ 14 | -v {{ lain_data_dir }}/rebellion/logs/filebeat:/var/log/filebeat/ \ 15 | -v {{ lain_data_dir }}/rebellion/logs/supervisor:/var/log/supervisor/ \ 16 | -v /var/log/:/var/log/syslog/:ro \ 17 | {{ rebellion_image }} 18 | ExecStop=/bin/bash -c '/usr/bin/docker stop %n || true' 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /playbooks/roles/registry-moosefs/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: registry 4 | action: "none" 5 | -------------------------------------------------------------------------------- /playbooks/roles/registry-moosefs/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | 2 | # system_volumes.registry is like "/var/lib/registry:/var/lib/registry" 3 | - set_fact: 4 | registry_datadir: "{{ system_volumes.registry.split(':')[0] }}" 5 | 6 | 7 | - include: build.yaml 8 | when: is_lain_manager 9 | 10 | - name: create registry's data-dir 11 | file: path="{{ registry_datadir }}" state=directory 12 | when: not is_lain_manager 13 | 14 | - name: generate systemd.mount for /var/lib/registry 15 | template: src=var-lib-registry.mount.j2 dest=/etc/systemd/system/var-lib-registry.mount 16 | notify: 17 | - reload systemd for registry 18 | - mount registry directory 19 | when: not is_lain_manager 20 | 21 | - meta: flush_handlers 22 | when: not is_lain_manager 23 | 24 | - name: ensure registry data dir mounted onto moosefs and enable this service 25 | service: name=var-lib-registry.mount state=started enabled=yes 26 | when: not is_lain_manager 27 | -------------------------------------------------------------------------------- /playbooks/roles/registry-moosefs/templates/var-lib-registry.mount.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=bind registry data dir onto moosefs 3 | After=network.target remote-fs.target mfs.mount 4 | 5 | [Mount] 6 | What={{ registry_mfsdir }} 7 | Where={{ registry_datadir }} 8 | Type=none 9 | Options=bind 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /playbooks/roles/registry/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for registry 2 | command: systemctl daemon-reload 3 | 4 | - name: mount registry directory 5 | service: name=var-lib-registry.mount state=restarted 6 | -------------------------------------------------------------------------------- /playbooks/roles/registry/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: libraries 4 | 5 | -------------------------------------------------------------------------------- /playbooks/roles/registry/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - include: push.yml 2 | when: action == 'push' 3 | 4 | # system_volumes.registry is like "/var/lib/registry:/var/lib/registry" 5 | - set_fact: 6 | registry_datadir: "{{ system_volumes.registry.split(':')[0] }}" 7 | when: action == 'usemfs' 8 | 9 | - name: create registry's data-dir 10 | file: path="{{ registry_datadir }}" state=directory 11 | when: action == 'usemfs' 12 | 13 | - name: generate systemd.mount for /var/lib/registry 14 | template: src=var-lib-registry.mount.j2 dest=/etc/systemd/system/var-lib-registry.mount 15 | notify: 16 | - reload systemd for registry 17 | - mount registry directory 18 | when: action == 'usemfs' 19 | 20 | - meta: flush_handlers 21 | when: action == 'usemfs' 22 | 23 | - name: ensure registry data dir mounted onto moosefs and enable this service 24 | service: name=var-lib-registry.mount state=started enabled=yes 25 | when: action == 'usemfs' 26 | -------------------------------------------------------------------------------- /playbooks/roles/registry/tasks/push.yml: -------------------------------------------------------------------------------- 1 | - name: restart calico-felix 2 | service: name=calico-felix state=restarted 3 | ignore_errors: yes 4 | 5 | - name: wait_for registry ready 6 | command: curl -m 2 http://registry.lain.local/v2/ 7 | register: result 8 | until: "result.stdout.startswith('{')" 9 | retries: 50 10 | delay: 5 11 | changed_when: False 12 | 13 | - name: push image to registry (may take minutes) 14 | docker_push: 15 | image: "{{ bootstrap_images[item] }}" 16 | registry: registry.lain.local 17 | with_items: "{{images_to_push|default([])}}" 18 | 19 | # vim: set filetype=ansible.yaml: 20 | -------------------------------------------------------------------------------- /playbooks/roles/registry/templates/registry.j2: -------------------------------------------------------------------------------- 1 | { 2 | "image": "{{ registry_image }}", 3 | "expose": 5000, 4 | "num_instances": 1, 5 | "memory": "128m", 6 | "env": 7 | [ 8 | "LAIN_APPNAME=registry", 9 | "LAIN_PROCNAME=web", 10 | "CALICO_IP=auto", 11 | "CALICO_PROFILE=default", 12 | "GUNICORN_OPTS=[--preload]" 13 | ], 14 | "annotation": "{\"mountpoint\": [\"registry.{{ domain }}\"]}" 15 | } 16 | -------------------------------------------------------------------------------- /playbooks/roles/registry/templates/var-lib-registry.mount.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=bind registry data dir onto moosefs 3 | After=network.target remote-fs.target mfs.mount 4 | 5 | [Mount] 6 | What={{ registry_mfsdir }} 7 | Where={{ registry_datadir }} 8 | Type=none 9 | Options=bind 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /playbooks/roles/remove-node/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: os-dependent-vars 3 | -------------------------------------------------------------------------------- /playbooks/roles/remove-node/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: check if having lain container running on this node 2 | shell: docker ps | grep -v .portal.portal | grep "registry.{{ domain }}" | wc -l 3 | register: result 4 | 5 | - fail: msg="still having lain container running on this node, check if by `docker ps`" 6 | when: result.stdout != '0' 7 | 8 | - name: stop networkd 9 | service: name=networkd state=stopped 10 | 11 | - name: stop the swarm agent 12 | service: name=swarm-agent state=stopped 13 | 14 | - name: stop the swarm manager 15 | service: name=swarm-manager state=stopped 16 | 17 | - name: stop lainlet 18 | service: name=lainlet state=stopped 19 | 20 | - name: stop rebellion 21 | service: name=rebellion state=stopped 22 | 23 | - name: get /lain/config/backup_enabled 24 | command: etcdctl get /lain/config/backup_enabled 25 | register: result 26 | ignore_errors: yes 27 | 28 | - name: stop backupd 29 | service: name=backupd state=stopped 30 | when: result|success and result.stdout == 'True' 31 | 32 | - name: umount /var/lib/registry 33 | command: umount /var/lib/registry 34 | ignore_errors: yes 35 | 36 | - name: umount /mfs 37 | command: umount /mfs 38 | ignore_errors: yes 39 | 40 | - name: stop rsyncd 41 | service: name="{{rsync_service_name}}" state=stopped 42 | 43 | - name: stop calico 44 | service: name={{item}} state=stopped 45 | with_items: 46 | - "calico-bird" 47 | - "calico-bird6" 48 | - "calico-confd" 49 | - "calico-felix" 50 | - "calico-libnetwork" 51 | 52 | - name: stop etcd 53 | service: name=etcd state=stopped 54 | 55 | - name: stop consul 56 | service: name=consul state=stopped 57 | -------------------------------------------------------------------------------- /playbooks/roles/rsync/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: os-dependent-vars 3 | - role: config 4 | -------------------------------------------------------------------------------- /playbooks/roles/rsync/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: install rsync 2 | package: name=rsync state=present 3 | 4 | - name: get network segment 5 | command: etcdctl get /lain/config/node_network 6 | register: network_result 7 | 8 | - set_fact: 9 | network: "{{network_result.stdout}}" 10 | 11 | - name: generate secret for rsyncd 12 | shell: etcdctl set /lain/config/rsyncd_secrets "$(uuidgen)" 13 | register: new_secrets_result 14 | when: bootstrapping is defined and bootstrapping|bool 15 | 16 | - name: get rsync secret from etcd 17 | command: etcdctl get /lain/config/rsyncd_secrets 18 | register: secrets_result 19 | 20 | - set_fact: 21 | rsyncd_secrets: "{{secrets_result.stdout}}" 22 | 23 | - fail: msg="secrets value needed, you can get it in /etc/rsyncd.secrets on manager-node" 24 | when: rsyncd_secrets == "" 25 | 26 | - name: set set secrets into etcd 27 | command: etcdctl set /lain/config/rsyncd_secrets "{{ rsyncd_secrets }}" 28 | when: bootstrapping|bool 29 | 30 | - name: write secrets 31 | template: src=rsyncd.secrets.j2 dest={{ rsync_secrets_file }} 32 | register: secrets_result 33 | 34 | - name: chmod secrets file 35 | file: path={{ rsync_secrets_file }} state=file mode=0400 group=root owner=root 36 | 37 | - name: generate rsyncd configuration 38 | template: src=rsyncd.conf.j2 dest="{{rsync_conf_path}}" 39 | register: config_result 40 | 41 | - name: generate service configration 42 | template: src=rsyncd.service.j2 dest={{ rsync_service_path }} 43 | register: service_result 44 | 45 | - name: reload systemd for rsync 46 | command: systemctl daemon-reload 47 | 48 | - name: restart the "{{ rsync_service_name }}" 49 | service: name="{{ rsync_service_name }}" enabled=yes state=restarted 50 | when: secrets_result|changed or config_result|changed or service_result|changed 51 | 52 | - name: ensure "{{ rsync_service_name }}" started 53 | service: name="{{ rsync_service_name }}" enabled=yes state=started 54 | -------------------------------------------------------------------------------- /playbooks/roles/rsync/templates/rsyncd.conf.j2: -------------------------------------------------------------------------------- 1 | use chroot = yes 2 | max connections = 4 3 | timeout = 900 4 | transfer logging = yes 5 | dont compress = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2 6 | 7 | [lain_volume] 8 | uid = root 9 | gid = root 10 | path = /data/lain/volumes 11 | comment = the volume host directory for lain 12 | read only = true 13 | hosts allow = {{ rsync_network }} 14 | auth users = {{ rsync_auth_user }} 15 | secrets file = {{ rsync_secrets_file }} 16 | -------------------------------------------------------------------------------- /playbooks/roles/rsync/templates/rsyncd.secrets.j2: -------------------------------------------------------------------------------- 1 | {{ rsync_auth_user }}:{{ rsyncd_secrets }} 2 | -------------------------------------------------------------------------------- /playbooks/roles/rsync/templates/rsyncd.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=fast remote file copy program daemon 3 | ConditionPathExists=/etc/rsyncd.conf 4 | 5 | [Service] 6 | ExecStart=/usr/bin/rsync --daemon --no-detach --config={{rsync_conf_path}} 7 | 8 | [Install] 9 | WantedBy=multi-user.target 10 | -------------------------------------------------------------------------------- /playbooks/roles/rsyslog-relocate/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: restart rsyslog 2 | service: name=rsyslog state=restarted -------------------------------------------------------------------------------- /playbooks/roles/rsyslog-relocate/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: relocate syslog messages 2 | template: src=syslog-messages.conf.j2 dest=/etc/rsyslog.d/syslog-messages.conf 3 | notify: 4 | - restart rsyslog 5 | 6 | - name: remove default logrotate settting in syslog if exists 7 | replace: dest=/etc/logrotate.d/syslog regexp='/var/log/messages' 8 | 9 | - name: reset syslog-messages logrotate config 10 | template: src=syslog-messages.j2 dest=/etc/logrotate.d/syslog-messages -------------------------------------------------------------------------------- /playbooks/roles/rsyslog-relocate/templates/syslog-messages.conf.j2: -------------------------------------------------------------------------------- 1 | *.info;mail.none;authpriv.none;cron.none {{ syslog_messages_location|default('/var/log/messages') }} 2 | & stop -------------------------------------------------------------------------------- /playbooks/roles/rsyslog-relocate/templates/syslog-messages.j2: -------------------------------------------------------------------------------- 1 | {{ syslog_messages_location|default('/var/log/messages') }} 2 | { 3 | rotate 7 4 | daily 5 | 6 | compress 7 | 8 | postrotate 9 | /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true 10 | endscript 11 | } -------------------------------------------------------------------------------- /playbooks/roles/rsyslog/files/rsyslog.conf: -------------------------------------------------------------------------------- 1 | # rsyslog configuration file 2 | 3 | #### MODULES #### 4 | 5 | #$ModLoad imudp 6 | #$UDPServerRun 514 7 | $ModLoad imtcp 8 | $InputTCPServerRun 514 9 | 10 | 11 | #### GLOBAL DIRECTIVES #### 12 | 13 | $WorkDirectory /var/lib/rsyslog 14 | $ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat 15 | $IncludeConfig /etc/rsyslog.d/*.conf 16 | 17 | 18 | #### RULES #### 19 | 20 | *.info;mail.none;authpriv.none;cron.none /var/log/messages 21 | -------------------------------------------------------------------------------- /playbooks/roles/rsyslog/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: restart rsyslog 2 | service: name=rsyslog state=restarted 3 | -------------------------------------------------------------------------------- /playbooks/roles/rsyslog/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: install rsyslog 2 | package: name=rsyslog state=present 3 | 4 | - name: copy rsyslog.conf 5 | copy: src=rsyslog.conf dest=/etc/rsyslog.conf backup=yes 6 | notify: 7 | - restart rsyslog 8 | 9 | - meta: flush_handlers 10 | -------------------------------------------------------------------------------- /playbooks/roles/ssl/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: manager 3 | -------------------------------------------------------------------------------- /playbooks/roles/ssl/tasks/ca.yaml: -------------------------------------------------------------------------------- 1 | # ref doc: https://help.github.com/enterprise/11.10.340/admin/articles/using-self-signed-ssl-certificates/ 2 | 3 | - file: path={{ manager_home }}/.certs state=directory mode=0700 4 | 5 | - name: generate the root CA key 6 | command: openssl genrsa -out {{ manager_home }}/.certs/rootCA.key 2048 7 | args: 8 | creates: "{{ manager_home }}/.certs/rootCA.key" 9 | 10 | - name: generate the self-signed root CA certificate 11 | # TODO notify sysadmin when the cert is near expiring 12 | command: openssl req -x509 -new -nodes -key {{ manager_home }}/.certs/rootCA.key -days 365 -out {{ manager_home }}/.certs/rootCA.crt -subj "/C=ZZ/O=Lain/CN={{ domain }}" 13 | args: 14 | creates: "{{ manager_home }}/.certs/rootCA.crt" 15 | 16 | - name: generate the wildcard SSL key for web 17 | command: openssl genrsa -out {{ manager_home }}/.certs/web.key 2048 18 | args: 19 | creates: "{{ manager_home }}/.certs/web.key" 20 | 21 | - name: generate the certificate signing request for web SSL key 22 | command: openssl req -new -key {{ manager_home }}/.certs/web.key -out {{ manager_home }}/.certs/web.csr -subj "/C=ZZ/O=Lain/CN=*.{{ domain }}" 23 | args: 24 | creates: "{{ manager_home }}/.certs/web.csr" 25 | 26 | - name: generate the signed certificate for web SSL 27 | # TODO notify sysadmin when the cert is near expiring 28 | command: > 29 | openssl x509 -req -in {{ manager_home }}/.certs/web.csr 30 | -CA {{ manager_home }}/.certs/rootCA.crt 31 | -CAkey {{ manager_home }}/.certs/rootCA.key 32 | -CAcreateserial -out {{ manager_home }}/.certs/web.crt 33 | -days 365 34 | args: 35 | creates: "{{ manager_home }}/.certs/web.crt" 36 | 37 | - file: path="{{ manager_home }}/.certs/{{ item }}" mode=0600 38 | with_items: 39 | - rootCA.key 40 | - rootCA.crt 41 | - web.key 42 | - web.csr 43 | - web.crt 44 | -------------------------------------------------------------------------------- /playbooks/roles/ssl/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - include: ca.yaml 2 | when: is_lain_manager 3 | 4 | - name: copy self-signed CA root(CentOS) 5 | copy: src="{{ manager_home }}/.certs/rootCA.crt" dest=/etc/pki/ca-trust/source/anchors/ 6 | when: ansible_distribution=="CentOS" 7 | 8 | - name: copy self-signed CA root(Ubuntu) 9 | copy: src="{{ manager_home }}/.certs/rootCA.crt" dest=/usr/local/share/ca-certificates/ 10 | when: ansible_distribution=="Ubuntu" 11 | 12 | - name: trust self-sigend CA root(CentOS) 13 | command: update-ca-trust extract 14 | when: ansible_distribution=="CentOS" 15 | 16 | - name: trust self-sigend CA root(Ubuntu) 17 | command: update-ca-certificates 18 | when: ansible_distribution=="Ubuntu" 19 | -------------------------------------------------------------------------------- /playbooks/roles/swarm-manage/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for swarm-manage 2 | command: systemctl daemon-reload 3 | 4 | - name: restart swarm agent 5 | service: name=swarm-agent state=restarted 6 | 7 | - name: restart swarm manager 8 | service: name=swarm-manager state=restarted 9 | -------------------------------------------------------------------------------- /playbooks/roles/swarm-manage/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config 3 | - role: docker 4 | - role: etcd 5 | - role: binary 6 | - role: images 7 | images: 8 | - swarm 9 | -------------------------------------------------------------------------------- /playbooks/roles/swarm-manage/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: config swarm agent 2 | template: src=swarm-agent.service.j2 dest=/etc/systemd/system/swarm-agent.service 3 | notify: 4 | - reload systemd for swarm-manage 5 | - restart swarm agent 6 | - meta: flush_handlers 7 | 8 | - name: ensure swarm agent started 9 | service: name=swarm-agent enabled=yes state=started 10 | 11 | - name: checking swarm agent working correctly 12 | command: etcdctl ls {{ swarm_discovery_path }}/{{ node_ip }}:{{ docker_port }} 13 | register: result 14 | until: result|success 15 | retries: 50 16 | delay: 5 17 | changed_when: False 18 | 19 | # every node may becomming a swarm-manager later, 20 | # add it into service to make administration convenient 21 | - name: add swarm manager service 22 | template: src=swarm-manager.service.j2 dest=/etc/systemd/system/swarm-manager.service 23 | notify: 24 | - reload systemd for swarm-manage 25 | - meta: flush_handlers 26 | 27 | - name: stop swarm manager 28 | service: name=swarm-manager state=stopped 29 | when: not is_swarm_manager 30 | 31 | # make sure the swarm manager started 32 | - include: swarm-manager.yaml 33 | when: is_swarm_manager 34 | -------------------------------------------------------------------------------- /playbooks/roles/swarm-manage/tasks/swarm-manager.yaml: -------------------------------------------------------------------------------- 1 | - name: config swarm manager 2 | template: src=swarm-manager.service.j2 dest=/etc/systemd/system/swarm-manager.service 3 | notify: 4 | - reload systemd for swarm-manage 5 | - restart swarm manager 6 | - meta: flush_handlers 7 | 8 | - name: ensure swarm manager started 9 | service: name=swarm-manager enabled=yes state=started 10 | 11 | - name: waiting for swarm manager to collect information from cluster nodes 12 | shell: "docker -H tcp://{{ node_ip }}:{{ swarm_manager_port }} info | grep 'Nodes: ' | awk '{ print $2 }'" 13 | register: swarm_cluster_nodes 14 | until: swarm_cluster_nodes.stdout|int > 0 15 | retries: 50 16 | delay: 5 17 | changed_when: False 18 | 19 | - name: set the swarm manager ip into etcd 20 | command: etcdctl set "{{ swarm_manager_ip_key }}" "{{ node_ip }}" 21 | -------------------------------------------------------------------------------- /playbooks/roles/swarm-manage/templates/swarm-agent.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=docker swarm agent 3 | After=docker.service 4 | 5 | [Service] 6 | ExecStartPre=-/usr/bin/docker stop %n 7 | ExecStartPre=-/usr/bin/docker rm %n 8 | ExecStart=/usr/bin/docker run \ 9 | --name %n \ 10 | --restart=always \ 11 | {{ swarm_image }} join \ 12 | --addr={{ node_ip }}:{{ docker_port }} \ 13 | etcd://{{ node_ip }}:{{ etcd_client_port }}/lain/swarm 14 | ExecStop=/bin/bash -c '/usr/bin/docker stop %n || true' 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /playbooks/roles/swarm-manage/templates/swarm-manager.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=docker swarm manager 3 | After=docker.service 4 | 5 | [Service] 6 | ExecStartPre=-/usr/bin/docker stop %n 7 | ExecStartPre=-/usr/bin/docker rm %n 8 | ExecStart=/usr/bin/docker run \ 9 | --name %n \ 10 | --restart=always \ 11 | -p {{ swarm_manager_port }}:{{ docker_port }} \ 12 | {{ swarm_image }} manage \ 13 | --replication \ 14 | --addr={{ node_ip }}:{{ swarm_manager_port }} \ 15 | etcd://{{ node_ip }}:{{ etcd_client_port }}/lain/swarm 16 | ExecStop=/bin/bash -c '/usr/bin/docker stop %n || true' 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /playbooks/roles/swarm-upgrade/handlers/main.yaml: -------------------------------------------------------------------------------- 1 | - name: reload systemd for swarm-manage 2 | command: systemctl daemon-reload 3 | 4 | - name: restart swarm agent 5 | service: name=swarm-agent state=restarted 6 | 7 | - name: stop swarm manager 8 | service: name=swarm-manager state=stopped 9 | 10 | - name: restart swarm manager 11 | service: name=swarm-manager state=restarted 12 | -------------------------------------------------------------------------------- /playbooks/roles/swarm-upgrade/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: config -------------------------------------------------------------------------------- /playbooks/roles/swarm-upgrade/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: pull new swarm image 2 | command: docker pull registry.lain.local/swarm:{{ upgrade_version }} 3 | 4 | - name: tag new swarm image 5 | command: docker tag registry.lain.local/swarm:{{ upgrade_version }} swarm:{{ upgrade_version }} 6 | 7 | - name: stop deployd 8 | service: name=deployd enabled=yes state=stopped 9 | ignore_errors: yes 10 | 11 | - name: config swarm agent 12 | template: src=swarm-agent.service.j2 dest=/etc/systemd/system/swarm-agent.service 13 | notify: 14 | - reload systemd for swarm-manage 15 | - restart swarm agent 16 | - meta: flush_handlers 17 | 18 | - name: ensure swarm agent started 19 | service: name=swarm-agent enabled=yes state=started 20 | 21 | - name: checking swarm agent working correctly 22 | command: etcdctl ls {{ swarm_discovery_path }}/{{ node_ip }}:{{ docker_port }} 23 | register: result 24 | until: result|success 25 | retries: 50 26 | delay: 5 27 | changed_when: False 28 | 29 | # every node may becomming a swarm-manager later, 30 | # add it into service to make administration convenient 31 | - name: add swarm manager service 32 | template: src=swarm-manager.service.j2 dest=/etc/systemd/system/swarm-manager.service 33 | notify: 34 | - reload systemd for swarm-manage 35 | - stop swarm manager 36 | when: node_name not in manager_list 37 | - meta: flush_handlers 38 | 39 | - name: config swarm manager 40 | template: src=swarm-manager.service.j2 dest=/etc/systemd/system/swarm-manager.service 41 | notify: 42 | - restart swarm manager 43 | - reload systemd for swarm-manage 44 | when: node_name in manager_list 45 | - meta: flush_handlers 46 | 47 | - name: ensure swarm manager started 48 | service: name=swarm-manager enabled=yes state=started 49 | when: node_name in manager_list 50 | 51 | - name: waiting for swarm manager to collect information from cluster nodes 52 | shell: "docker -H swarm.lain:{{ swarm_manager_port }} info | grep 'Nodes: ' | awk '{ print $2 }'" 53 | register: swarm_cluster_nodes 54 | until: swarm_cluster_nodes.stdout|int == {{ groups['nodes'] | length }} 55 | retries: 50 56 | delay: 5 57 | changed_when: False 58 | 59 | - name: start deployd 60 | service: name=deployd enabled=yes state=started 61 | ignore_errors: yes 62 | -------------------------------------------------------------------------------- /playbooks/roles/swarm-upgrade/templates/swarm-agent.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=docker swarm agent 3 | After=docker.service 4 | 5 | [Service] 6 | ExecStart=/usr/bin/docker run \ 7 | --name %n \ 8 | --rm \ 9 | swarm:{{ upgrade_version }} join \ 10 | --addr={{ node_ip }}:{{ docker_port }} \ 11 | etcd://{{ node_ip }}:{{ etcd_client_port }}/lain/swarm 12 | ExecStop=/usr/bin/docker stop %n 13 | Restart=always 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /playbooks/roles/swarm-upgrade/templates/swarm-manager.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=docker swarm manager 3 | After=docker.service 4 | 5 | [Service] 6 | ExecStart=/usr/bin/docker run \ 7 | --name %n \ 8 | --rm \ 9 | -p {{ swarm_manager_port }}:{{ docker_port }} \ 10 | swarm:{{ upgrade_version }} manage \ 11 | --replication \ 12 | --addr={{ node_ip }}:{{ swarm_manager_port }} \ 13 | etcd://{{ node_ip }}:{{ etcd_client_port }}/lain/swarm 14 | ExecStop=/usr/bin/docker stop %n 15 | Restart=always 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /playbooks/roles/swarm/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: swarm-manage 3 | -------------------------------------------------------------------------------- /playbooks/roles/systemd/files/journald.conf.d/lain.conf: -------------------------------------------------------------------------------- 1 | [Journal] 2 | # new ver systemd bug: https://github.com/systemd/systemd/pull/1662 3 | Compress=no 4 | 5 | # Explicitly use persistent storage 6 | Storage=persistent 7 | 8 | # Explicitly control log forwarding flow, since the default configuration of 9 | # systemd is prone to change 10 | ForwardToSyslog=no 11 | ForwardToKMsg=no 12 | ForwardToConsole=no 13 | #ForwardToWall=yes 14 | -------------------------------------------------------------------------------- /playbooks/roles/systemd/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: tune journald config 2 | copy: src=journald.conf.d dest=/etc/systemd/ 3 | register: result 4 | 5 | - name: reload systemd 6 | command: systemctl daemon-reload 7 | when: result|changed 8 | 9 | - name: restart journald 10 | service: name=systemd-journald state=restarted 11 | when: result|changed 12 | -------------------------------------------------------------------------------- /playbooks/roles/webrouter-start/meta/main.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - role: firewall 3 | - role: webrouter 4 | - role: console-deploy 5 | app: webrouter 6 | -------------------------------------------------------------------------------- /playbooks/roles/webrouter-start/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: set webrouter virtual ip config 2 | set_virtual_ip_key: ip="{{ vip }}" port="{{ item }}" container_app="webrouter" container_proc="worker" 3 | with_items: 4 | - 80 5 | - 443 6 | - 8080 7 | - 8443 8 | 9 | - meta: flush_handlers 10 | -------------------------------------------------------------------------------- /playbooks/roles/webrouter/files/nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | server_name everythingelse; 4 | 5 | #charset koi8-r; 6 | access_log /var/log/nginx/default.access.log main; 7 | 8 | location / { 9 | return 404; 10 | } 11 | 12 | #error_page 404 /404.html; 13 | 14 | # redirect server error pages to the static page /50x.html 15 | # 16 | error_page 500 502 503 504 /50x.html; 17 | location = /50x.html { 18 | root /usr/share/nginx/html; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /playbooks/roles/webrouter/files/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | user nginx; 3 | worker_processes 4; 4 | 5 | error_log /var/log/nginx/error.log warn; 6 | pid /var/run/nginx.pid; 7 | 8 | 9 | events { 10 | worker_connections 10240; 11 | } 12 | 13 | 14 | http { 15 | include /etc/nginx/mime.types; 16 | default_type application/octet-stream; 17 | server_names_hash_bucket_size 512; 18 | real_ip_header "X-Forwarded-For"; 19 | set_real_ip_from 10.0.0.0/8; 20 | set_real_ip_from 172.20.0.0/16; 21 | real_ip_recursive on; 22 | 23 | log_format main '$remote_addr@$remote_user@[$time_local]@$Host@"$request"@' 24 | '$status@$body_bytes_sent@"$http_referer"@' 25 | '$http_user_agent@$http_x_forwarded_for@' 26 | 'upstream_response_time@$upstream_response_time@request_time@$request_time'; 27 | 28 | large_client_header_buffers 4 1024k; 29 | client_max_body_size 0; 30 | chunked_transfer_encoding on; 31 | 32 | access_log /var/log/nginx/access.log main; 33 | 34 | sendfile on; 35 | #tcp_nopush on; 36 | 37 | keepalive_timeout 65; 38 | 39 | #gzip on; 40 | 41 | include /etc/nginx/upstreams/*.upstreams; 42 | include /etc/nginx/conf.d/*.conf; 43 | include /etc/nginx/default.conf; 44 | } 45 | -------------------------------------------------------------------------------- /playbooks/roles/webrouter/files/nginx/proxy.conf: -------------------------------------------------------------------------------- 1 | proxy_next_upstream error invalid_header http_502; 2 | 3 | proxy_redirect off; 4 | proxy_set_header Host $host; 5 | proxy_set_header X-Real-IP $remote_addr; 6 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 7 | proxy_set_header X-Original-URI $request_uri; 8 | proxy_set_header REQUEST_URI $request_uri; 9 | 10 | set $xproto $scheme; 11 | if ($http_x_forwarded_proto ~* "^http") { 12 | set $xproto $http_x_forwarded_proto; 13 | } 14 | proxy_set_header X-Forwarded-Proto $xproto; 15 | 16 | proxy_http_version 1.1; 17 | proxy_set_header Upgrade $http_upgrade; 18 | proxy_set_header Connection "upgrade"; 19 | 20 | #client_max_body_size 10m; 21 | client_body_buffer_size 128k; 22 | proxy_connect_timeout 90; 23 | proxy_send_timeout 90; 24 | proxy_read_timeout 900; 25 | proxy_buffer_size 32k; 26 | proxy_buffers 4 32k; 27 | proxy_busy_buffers_size 64k; 28 | proxy_temp_file_write_size 64k; 29 | -------------------------------------------------------------------------------- /playbooks/roles/webrouter/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: prepare webrouter volume dirs 2 | file: path={{ lain_data_dir }}/volumes/webrouter/webrouter.worker.worker/1{{ item }} state=directory 3 | with_items: 4 | - /var/log/nginx 5 | - /var/log/watcher 6 | - /var/log/supervisor 7 | - /etc/nginx/ssl 8 | - /etc/nginx/conf.d 9 | - /etc/nginx/upstreams 10 | - /etc/nginx/locations 11 | - /etc/nginx/buffer 12 | 13 | - name: copy configs to webrouter volumes 14 | copy: src={{ item.src }} dest={{ lain_data_dir }}/volumes/webrouter/webrouter.worker.worker/1{{ item.dest }} 15 | with_items: 16 | - src: nginx/proxy.conf 17 | dest: /etc/nginx/proxy.conf 18 | - src: nginx/nginx.conf 19 | dest: /etc/nginx/nginx.conf 20 | - src: nginx/default.conf 21 | dest: /etc/nginx/default.conf 22 | 23 | - name: load ssl info from etcd 24 | command: etcdctl get /lain/config/ssl 25 | register: result 26 | 27 | - name: copy ssl crt to volumes 28 | copy: src={{ manager_home }}/.certs/{{ item.value }}.crt dest={{ lain_data_dir }}/volumes/webrouter/webrouter.worker.worker/1/etc/nginx/ssl/ owner=root 29 | with_dict: "{{result.stdout|from_json}}" 30 | when: result|success and result.stdout != "" 31 | 32 | - name: copy ssl key to volumes 33 | copy: src={{ manager_home }}/.certs/{{ item.value }}.key dest={{ lain_data_dir }}/volumes/webrouter/webrouter.worker.worker/1/etc/nginx/ssl/ owner=root 34 | with_dict: "{{result.stdout|from_json}}" 35 | when: result|success and result.stdout != "" 36 | -------------------------------------------------------------------------------- /playbooks/site.yaml: -------------------------------------------------------------------------------- 1 | - hosts: "{{ target|default('nodes') }}" 2 | roles: 3 | - node 4 | --------------------------------------------------------------------------------