├── .gitignore ├── .travis.yml ├── Dockerfile ├── Makefile ├── README.md ├── config_ip.yml ├── env_make ├── group_vars ├── all └── all.save ├── init-fake.conf ├── maas_api_test.py ├── playbook.yml ├── roles ├── maas-os │ ├── tasks │ │ └── main.yml │ └── vars │ │ └── main.yml ├── maas-settings │ ├── defaults │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── preseed.txt.j2 └── maasetherwakehack │ ├── tasks │ └── main.yml │ └── templates │ ├── 99-maas-sudoers.j2 │ └── ether_wake.template.j2 ├── rsyslog.conf └── volumes └── Dockerfile /.gitignore: -------------------------------------------------------------------------------- 1 | ip.txt 2 | api_key.txt 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: "2.7" 4 | sudo: required 5 | dist: trusty 6 | 7 | before_install: 8 | - sudo apt-get update 9 | - sudo apt-get install -o Dpkg::Options::="--force-confold" --force-yes -y docker-engine 10 | - virtualenv venv 11 | - pip install https://github.com/mvdbeek/maasclient/archive/master.zip 12 | - make 13 | 14 | script: 15 | - docker network create --subnet=192.168.1.0/24 net1 16 | - make run -e NET=net1 && sleep 120s 17 | - make test_api_access 18 | 19 | deploy: 20 | provider: script 21 | script: make push DOCKER_PASSWORD=$DOCKER_PASSWORD 22 | on: 23 | branch: master 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | 3 | # Based on https://github.com/tianon/dockerfiles/blob/4d24a12b54b75b3e0904d8a285900d88d3326361/sbin-init/ubuntu/upstart/14.04/Dockerfile 4 | 5 | ADD init-fake.conf /etc/init/fake-container-events.conf 6 | 7 | ADD . /setup 8 | 9 | # remove pointless stuff and run maas playbook 10 | RUN rm /usr/sbin/policy-rc.d; \ 11 | rm /sbin/initctl; dpkg-divert --rename --remove /sbin/initctl && \ 12 | locale-gen en_US.UTF-8 && update-locale LANG=en_US.UTF-8 && \ 13 | /usr/sbin/update-rc.d -f ondemand remove; \ 14 | for f in \ 15 | /etc/init/u*.conf \ 16 | /etc/init/mounted-dev.conf \ 17 | /etc/init/mounted-proc.conf \ 18 | /etc/init/mounted-run.conf \ 19 | /etc/init/mounted-tmp.conf \ 20 | /etc/init/mounted-var.conf \ 21 | /etc/init/tty*.conf \ 22 | /etc/init/hwclock*.conf \ 23 | ; do \ 24 | dpkg-divert --local --rename --add "$f"; \ 25 | done; \ 26 | echo '# /lib/init/fstab: cleared out for bare-bones Docker' > /lib/init/fstab && \ 27 | apt-get update -qq && \ 28 | apt-get install -y apt-transport-https software-properties-common && \ 29 | apt-add-repository -y ppa:ansible/ansible && \ 30 | apt-get update -qq && apt-get install -y --no-install-recommends ansible && \ 31 | ansible-playbook -i /setup/hosts.ini -c local --tags install_packages /setup/playbook.yml 32 | 33 | # let Upstart know it's in a container 34 | ENV container docker 35 | 36 | # pepare for takeoff 37 | CMD ["/sbin/init"] 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include env_make 2 | NS = artbio 3 | VERSION ?= latest 4 | 5 | REPO = maas 6 | NAME = maas 7 | INSTANCE = default 8 | 9 | .PHONY: build push shell run start stop rm release 10 | build: 11 | docker build -t $(NS)/$(REPO):$(VERSION) . 12 | docker run -d --name $(NAME)-$(INSTANCE) --privileged $(NS)/$(REPO):$(VERSION) 13 | docker exec $(NAME)-$(INSTANCE) ansible-playbook -i localhost, -c local /setup/playbook.yml 14 | docker exec $(NAME)-$(INSTANCE) cp /setup/rsyslog.conf /etc/init/rsyslog.conf 15 | make commit 16 | make stop 17 | make rm 18 | docker build -t $(NS)/$(REPO):$(VERSION) volumes 19 | login: 20 | @docker login -e=$(DOCKER_EMAIL) -u=$(DOCKER_USERNAME) -p=$(DOCKER_PASSWORD) 21 | 22 | push: 23 | make login 24 | docker push $(NS)/$(REPO):$(VERSION) 25 | 26 | commit: 27 | docker commit $(NAME)-$(INSTANCE) $(NS)/$(REPO):$(VERSION) 28 | 29 | shell: 30 | docker exec -i -t $(NAME)-$(INSTANCE) /bin/bash 31 | 32 | run: 33 | docker run -d --net $(NET) --privileged --name $(NAME)-$(INSTANCE) $(ENV) $(NS)/$(REPO):$(VERSION) 34 | 35 | get_api_key: 36 | docker exec $(NAME)-$(INSTANCE) maas-region-admin apikey --username maas > api_key.txt 37 | 38 | get_ip_address: 39 | docker inspect --format '{{ .NetworkSettings.Networks.net1.IPAddress }}' maas-default > ip.txt 40 | 41 | test_api_access: 42 | make get_api_key 43 | make get_ip_address 44 | python maas_api_test.py --api_key `cat api_key.txt` --api_url http://`cat ip.txt`/MAAS/api/1.0 | grep `cat ip.txt` 45 | 46 | persistent_volumes: 47 | 48 | docker create --name maas_persistent_data -v /etc/maas -v /var/lib/maas -v /var/lib/postgresql -v /var/log $(NS)/$(REPO):$(VERSION) /bin/true 49 | 50 | delete_persistent_volumes: 51 | 52 | docker rm maas_persitent_data 53 | 54 | run_persistent: 55 | 56 | docker run -d --net $(NET) --restart=always --privileged --volumes-from maas_persistent_data --name $(NAME)-$(INSTANCE) $(ENV) $(NS)/$(REPO):$(VERSION) 57 | 58 | stop: 59 | docker stop $(NAME)-$(INSTANCE) 60 | 61 | rm: 62 | make stop 63 | docker rm $(NAME)-$(INSTANCE) 64 | 65 | release: build 66 | make push -e VERSION=$(VERSION) 67 | 68 | default: build 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Docker MaaS 2 | ---- 3 | [![Build Status](https://travis-ci.org/ARTbio/maas-docker.svg?branch=master)](https://travis-ci.org/ARTbio/maas-docker) 4 | 5 | 6 | This repo contains a Dockerfile and an ansible playbook to build a Docker image 7 | with Canonical's MaaS. The build procedure is a little different from usual Dockerfiles, 8 | because we have to start the container in privileged mode to finish building. 9 | 10 | To build the container, type 11 | 12 | ``` 13 | make build 14 | ``` 15 | 16 | This will build a first docker image, then run that image in `--privileged` mode to finish the installation. 17 | To run the image, create a persistent volume: 18 | 19 | ``` 20 | make persistent_data 21 | ``` 22 | 23 | Then start the image 24 | 25 | ``` 26 | make run_persistent 27 | ``` 28 | 29 | If you want to boot pysical nodes with MaaS create a docker network that contains a physical interface. 30 | Assuming your physical network is on the 192.168.1.0/24 subnet, create a new docker network with 31 | 32 | ``` 33 | docker network create --subnet=192.168.1.0/24 net1 34 | ``` 35 | 36 | The network net1 will then show up in the list of docker networks: 37 | 38 | ``` 39 | $ docker network ls 40 | NETWORK ID NAME DRIVER 41 | c93f2f6e09df bridge bridge 42 | f1013ec4aa62 host host 43 | 094a5738492a net1 bridge 44 | a9c220157a01 none null 45 | 9ff0cc43cec6 sentrycompose_default bridge 46 | ``` 47 | and in the list of bridges: 48 | ``` 49 | $ brctl show 50 | bridge name bridge id STP enabled interfaces 51 | br-094a5738492a 8000.0242ea91311f no eth1 52 | ``` 53 | 54 | You can add physical devices (e.g. eth0) by typing 55 | 56 | ``` 57 | brctl addif br-094a5738492a eth0 58 | ``` 59 | 60 | Now stop the running container and start a new container inside the newly created docker container: 61 | ``` 62 | make rm # stops and removes container 63 | make run_persistent -e NET=net1 64 | ``` 65 | 66 | You should be able to deploy machines in MaaS now. 67 | -------------------------------------------------------------------------------- /config_ip.yml: -------------------------------------------------------------------------------- 1 | - name: Configure IP 2 | hosts: all 3 | vars: 4 | roles: 5 | - role: maas-settings 6 | -------------------------------------------------------------------------------- /env_make: -------------------------------------------------------------------------------- 1 | NET = "" 2 | 3 | DOCKER_EMAIL = artbio.ibps@gmail.com 4 | DOCKER_USERNAME = artbio 5 | 6 | ENV = \ 7 | -e SOME_KEY=SOME_VALUE 8 | -------------------------------------------------------------------------------- /group_vars/all: -------------------------------------------------------------------------------- 1 | maas_ip: "{{ facter_ipaddress }}" 2 | -------------------------------------------------------------------------------- /group_vars/all.save: -------------------------------------------------------------------------------- 1 | maas_ip: {{ }} 2 | -------------------------------------------------------------------------------- /init-fake.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | dockerfiles/init-fake.conf at master · tianon/dockerfiles · GitHub 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 | Skip to content 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 157 | 158 | 159 | 160 |
161 | 162 |
163 |
164 | 165 | 166 |
167 |
168 |
169 | 170 |
171 |
172 | 173 | 174 | 175 | 217 | 218 |

219 | 220 | /dockerfiles 223 | 224 |

225 | 226 |
227 |
228 | 229 | 270 | 271 |
272 |
273 | 274 |
275 |
276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 |
284 | 285 |
286 | 292 | 293 | 348 |
349 | 350 |
351 | 355 | Find file 356 | 357 | 358 |
359 | 362 |
363 | 364 | 365 |
366 | 367 | 368 | c4f8611 369 | 370 | Jun 6, 2014 371 | 372 | 377 | 378 |
379 | 383 | 384 |
385 | 386 | 395 |
396 | 397 |
398 |
399 |
400 | 401 |
402 | Raw 403 | Blame 404 | History 405 |
406 | 407 | 408 | 412 | 416 |
417 | 418 |
419 | 14 lines (10 sloc) 420 | 421 | 351 Bytes 422 |
423 |
424 | 425 | 426 | 427 |
428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 |
# fake some events needed for correct startup other services
436 |
description "In-Container Upstart Fake Events"
445 |
start on startup
454 |
script
rm -rf /var/run/*.pid
rm -rf /var/run/network/*
/sbin/initctl emit stopped JOB=udevtrigger --no-wait
/sbin/initctl emit started JOB=udev --no-wait
/sbin/initctl 2 --no-wait
end script
485 | 486 |
487 | 488 |
489 | 490 | 491 | 496 | 497 |
498 | 499 |
500 | 501 | 502 |
503 |
504 | 505 |
506 | 507 | 532 | 533 | 534 | 535 | 536 | 537 | 538 |
539 | 540 | 543 | Something went wrong with that request. Please try again. 544 |
545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 561 | 570 | 571 | 572 | 573 | 574 | -------------------------------------------------------------------------------- /maas_api_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import argparse 3 | from maasclient.auth import MaasAuth 4 | from maasclient import MaasClient 5 | 6 | parser = argparse.ArgumentParser() 7 | parser.add_argument("--api_key") 8 | parser.add_argument("--api_url") 9 | args = parser.parse_args() 10 | 11 | auth = MaasAuth(api_url=args.api_url, api_key=args.api_key) 12 | maas_client = MaasClient(auth) 13 | print(maas_client.server_hostname) 14 | -------------------------------------------------------------------------------- /playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Install MAAS 2 | remote_user: root 3 | hosts: all 4 | vars: 5 | maas_username: maas 6 | maas_password: maas 7 | maas_email: maas@artbio.fr 8 | maas_cluster_eth: eth0 9 | node_ip: "{{ hostvars.node_ip }}" 10 | ansible_dir: /var/ansible 11 | ansible_state_dir: "{{ ansible_dir }}/state" 12 | ansible_src_dir: "{{ ansible_dir }}/src" 13 | maas_startup_state: "{{ ansible_state_dir }}/maas-region-admin" 14 | roles: 15 | - role: maas-os 16 | - role: maasetherwakehack 17 | -------------------------------------------------------------------------------- /roles/maas-os/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Install system packages 3 | # From https://github.com/lgfausak/maas-ansible-playbook/blob/master/roles/maasserver/tasks/main.yml 4 | 5 | - file: path={{ ansible_state_dir }} state=directory 6 | 7 | - name: Install maas dependencies 8 | sudo: True 9 | apt: name={{ item }} update_cache=yes cache_valid_time=3600 state=installed install_recommends=yes 10 | with_items: 11 | - postgresql 12 | - apache2 13 | - bind9 14 | - puppet 15 | - rpcbind 16 | - chef 17 | - resolvconf 18 | - rpcbind 19 | - rsyslog 20 | - software-properties-common 21 | - etherwake 22 | tags: 23 | - install_packages 24 | 25 | - name: Update repository 26 | sudo: True 27 | apt_repository: repo=ppa:maas/stable 28 | tags: 29 | - install_packages 30 | 31 | - name: ensure postgres starts on a fresh reboot 32 | service: name={{ item }} state=started enabled=yes 33 | with_items: 34 | - postgresql 35 | - apache2 36 | - bind9 37 | - chef-client 38 | - puppet 39 | - rpcbind 40 | - resolvconf 41 | - rsyslog 42 | - rpcbind 43 | 44 | - name: Install MaaS 45 | sudo: True 46 | apt: name={{ item }} update_cache=yes cache_valid_time=3600 state=installed install_recommends=yes 47 | with_items: 48 | - maas 49 | - maas-dhcp 50 | - maas-dns 51 | ignore_errors: yes 52 | tags: 53 | - install_packages 54 | 55 | - name: Create Initial Maas Region Admin user 56 | sudo: True 57 | command: /usr/sbin/maas-region-admin createadmin --username={{ maas_username }} --password={{ maas_password }} --email={{ maas_email }} creates={{ maas_startup_state }} 58 | 59 | - name: Create Initial Maas Region Admin user semaphore 60 | sudo: True 61 | command: creates={{ maas_startup_state }} touch {{ maas_startup_state }} 62 | -------------------------------------------------------------------------------- /roles/maas-os/vars/main.yml: -------------------------------------------------------------------------------- 1 | maas_startup_state: "{{ ansible_state_dir }}/maas-region-admin" -------------------------------------------------------------------------------- /roles/maas-settings/defaults/main.yml: -------------------------------------------------------------------------------- 1 | maas_url: "{{ maas_ip }}/MAAS" 2 | maas_ip: 127.0.0.1 3 | -------------------------------------------------------------------------------- /roles/maas-settings/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Create preseed with correct IP 2 | template: src=preseed.txt.j2 dest=/tmp/preseed.txt 3 | 4 | - name: Set preseed 5 | command: debconf-set-selections /tmp/preseed.txt 6 | 7 | - name: Reconfigure region and cluster controller 8 | command: dpkg-reconfigure maas-cluster-controller maas-region-controller 9 | environment: 10 | DEBIAN_FRONTEND: noninteractive 11 | DEBCONF_NONINTERACTIVE_SEEN: true 12 | -------------------------------------------------------------------------------- /roles/maas-settings/templates/preseed.txt.j2: -------------------------------------------------------------------------------- 1 | maas-cluster-controller maas-cluster-controller/maas-url string {{ maas_url }} 2 | maas-region-controller-min maas/default-maas-url string {{ maas_ip }} 3 | -------------------------------------------------------------------------------- /roles/maasetherwakehack/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: hack on etherwake 2 | sudo: True 3 | template: src=ether_wake.template.j2 dest=/etc/maas/templates/power/ether_wake.template 4 | 5 | - name: modify sudoers for maas 6 | sudo: True 7 | template: src=99-maas-sudoers.j2 dest=/etc/sudoers.d/99-maas-sudoers mode="u=r,g=r" owner="root" group="root" 8 | -------------------------------------------------------------------------------- /roles/maasetherwakehack/templates/99-maas-sudoers.j2: -------------------------------------------------------------------------------- 1 | maas ALL= NOPASSWD: /usr/sbin/service maas-dhcpd start 2 | maas ALL= NOPASSWD: /usr/sbin/service maas-dhcpd status 3 | maas ALL= NOPASSWD: /usr/sbin/service maas-dhcpd restart 4 | maas ALL= NOPASSWD: /usr/sbin/service maas-dhcpd stop 5 | maas ALL= NOPASSWD: /usr/sbin/service maas-dhcpd6 start 6 | maas ALL= NOPASSWD: /usr/sbin/service maas-dhcpd6 status 7 | maas ALL= NOPASSWD: /usr/sbin/service maas-dhcpd6 restart 8 | maas ALL= NOPASSWD: /usr/sbin/service maas-dhcpd6 stop 9 | maas ALL= NOPASSWD: /usr/sbin/service tgt status 10 | maas ALL= NOPASSWD: /usr/sbin/service tgt start 11 | maas ALL= NOPASSWD: /usr/sbin/maas-provision 12 | maas ALL= NOPASSWD: SETENV: /usr/sbin/tgt-admin, /usr/bin/uec2roottar 13 | maas ALL= NOPASSWD: /usr/sbin/etherwake 14 | -------------------------------------------------------------------------------- /roles/maasetherwakehack/templates/ether_wake.template.j2: -------------------------------------------------------------------------------- 1 | # -*- mode: shell-script -*- 2 | # 3 | # Control node power through WOL, via `wakeonlan` or `etherwake`. 4 | # Do the 'off' with ssh shutdown (hack alert) 5 | # 6 | 7 | {% raw %} 8 | mac_address={{mac_address}} 9 | ip_address={{ip_address}} 10 | power_change={{power_change}} 11 | 12 | logger mac_address $mac_address, ip_address $ip_address, power_change $power_change 13 | 14 | if [ "${power_change}" = 'off' ] 15 | then 16 | logger executing ssh $ip_address -l ubuntu sudo shutdown -h now 17 | ssh $ip_address -l ubuntu sudo shutdown -h now 18 | logger ssh exited with $? 19 | elif [ "${power_change}" = 'on' ] 20 | then 21 | if [ -x /usr/sbin/etherwake ] 22 | then 23 | sudo /usr/sbin/etherwake -i {% endraw %}{{ maas_cluster_eth }}{% raw %} $mac_address 24 | else 25 | echo "No wakeonlan or etherwake program found." >&2 26 | fi 27 | else 28 | echo "There is no way to power down a node through etherwake." >&2 29 | exit 1 30 | fi 31 | 32 | exit 0 33 | {% endraw %} 34 | -------------------------------------------------------------------------------- /rsyslog.conf: -------------------------------------------------------------------------------- 1 | # rsyslog - system logging daemon 2 | # 3 | # rsyslog is an enhanced multi-threaded replacement for the traditional 4 | # syslog daemon, logging messages from applications 5 | 6 | description "system logging daemon" 7 | 8 | start on filesystem 9 | stop on runlevel [06] 10 | 11 | expect fork 12 | respawn 13 | 14 | pre-start script 15 | /sbin/init 5 16 | service postgresql start || true 17 | service apache2 start || true 18 | service bind9 start || true 19 | service puppet start || true 20 | service chef-client start || true 21 | initctl start maas-regiond || true 22 | initctl start maas-clusterd || true 23 | initctl start maas-proxy || true 24 | rsyslogd || true 25 | /usr/bin/ansible-playbook -i localhost, -c local /setup/config_ip.yml > /tmp/log.out 26 | /lib/init/apparmor-profile-load usr.sbin.rsyslogd 27 | end script 28 | 29 | script 30 | . /etc/default/rsyslog 31 | exec rsyslogd $RSYSLOGD_OPTIONS 32 | end script 33 | 34 | -------------------------------------------------------------------------------- /volumes/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM artbio/maas:latest 2 | 3 | VOLUME ["/etc/maas", "/var/log", "/var/lib/postgresql", "/var/lib/maas"] 4 | CMD ["/sbin/init"] 5 | --------------------------------------------------------------------------------