From f468fd3e349117ece96c73a7174af89d55e24242 Mon Sep 17 00:00:00 2001 From: Paul-Henri Froidmont Date: Wed, 26 Sep 2018 04:40:24 +0200 Subject: [PATCH] Deploy ingress, lego and dashboard --- group_vars/all/vault | 172 +++++----- group_vars/k8s | 4 + group_vars/k8s_proxy | 3 +- k8s.yml | 62 +--- library/kube.py | 300 ++++++++++++++++++ roles/ingress/defaults/main.yml | 10 + roles/ingress/tasks/main.yml | 97 ++++++ .../default-backend-controller.yml.j2 | 39 +++ .../templates/default-backend-service.yml.j2 | 13 + roles/ingress/templates/keepalived.yml.j2 | 37 +++ .../templates/nginx-ingress-clusterole.yml.j2 | 14 + .../nginx-ingress-clusterolebinding.yml.j2 | 12 + .../templates/nginx-ingress-configmap.yml.j2 | 10 + .../templates/nginx-ingress-controller.yml.j2 | 66 ++++ .../ingress/templates/nginx-ingress-sa.yml.j2 | 5 + .../templates/nginx-ingress-service.yml.j2 | 15 + roles/ingress/templates/notify.sh.j2 | 35 ++ roles/kubernetes-dashboard/defaults/main.yml | 15 + .../files/dashboard-clusterrolebinding.yml | 12 + .../files/dashboard-role.yml | 20 ++ .../files/dashboard-rolebinding.yml | 13 + .../files/dashboard-sa.yml | 7 + .../files/dashboard-secret.yml | 8 + .../files/dashboard-service.yml | 13 + .../files/heapster-clusterrolebinding.yml | 16 + .../files/heapster-deployment.yml | 77 +++++ .../files/heapster-role.yml | 24 ++ .../files/heapster-rolebinding.yml | 16 + .../files/heapster-sa.yml | 7 + .../files/heapster-service.yml | 15 + roles/kubernetes-dashboard/tasks/main.yml | 105 ++++++ .../templates/dashboard-deployment.yml.j2 | 47 +++ .../templates/dashboard-ingress.yml.j2 | 30 ++ roles/kubernetes/tasks/main.yml | 2 +- roles/lego/defaults/main.yml | 4 + roles/lego/tasks/main.yml | 40 +++ roles/lego/templates/lego-clusterole.yml.j2 | 14 + .../templates/lego-clusterolebinding.yml.j2 | 12 + roles/lego/templates/lego-configmap.yml.j2 | 10 + roles/lego/templates/lego-controller.yml.j2 | 48 +++ roles/lego/templates/lego-namespace.yml.j2 | 4 + roles/lego/templates/lego-sa.yml.j2 | 5 + scripts/scw_inventory.py | 5 +- 43 files changed, 1321 insertions(+), 142 deletions(-) create mode 100644 library/kube.py create mode 100644 roles/ingress/defaults/main.yml create mode 100644 roles/ingress/tasks/main.yml create mode 100644 roles/ingress/templates/default-backend-controller.yml.j2 create mode 100644 roles/ingress/templates/default-backend-service.yml.j2 create mode 100644 roles/ingress/templates/keepalived.yml.j2 create mode 100644 roles/ingress/templates/nginx-ingress-clusterole.yml.j2 create mode 100644 roles/ingress/templates/nginx-ingress-clusterolebinding.yml.j2 create mode 100644 roles/ingress/templates/nginx-ingress-configmap.yml.j2 create mode 100644 roles/ingress/templates/nginx-ingress-controller.yml.j2 create mode 100644 roles/ingress/templates/nginx-ingress-sa.yml.j2 create mode 100644 roles/ingress/templates/nginx-ingress-service.yml.j2 create mode 100644 roles/ingress/templates/notify.sh.j2 create mode 100644 roles/kubernetes-dashboard/defaults/main.yml create mode 100644 roles/kubernetes-dashboard/files/dashboard-clusterrolebinding.yml create mode 100644 roles/kubernetes-dashboard/files/dashboard-role.yml create mode 100644 roles/kubernetes-dashboard/files/dashboard-rolebinding.yml create mode 100644 roles/kubernetes-dashboard/files/dashboard-sa.yml create mode 100644 roles/kubernetes-dashboard/files/dashboard-secret.yml create mode 100644 roles/kubernetes-dashboard/files/dashboard-service.yml create mode 100644 roles/kubernetes-dashboard/files/heapster-clusterrolebinding.yml create mode 100644 roles/kubernetes-dashboard/files/heapster-deployment.yml create mode 100644 roles/kubernetes-dashboard/files/heapster-role.yml create mode 100644 roles/kubernetes-dashboard/files/heapster-rolebinding.yml create mode 100644 roles/kubernetes-dashboard/files/heapster-sa.yml create mode 100644 roles/kubernetes-dashboard/files/heapster-service.yml create mode 100644 roles/kubernetes-dashboard/tasks/main.yml create mode 100644 roles/kubernetes-dashboard/templates/dashboard-deployment.yml.j2 create mode 100644 roles/kubernetes-dashboard/templates/dashboard-ingress.yml.j2 create mode 100644 roles/lego/defaults/main.yml create mode 100644 roles/lego/tasks/main.yml create mode 100644 roles/lego/templates/lego-clusterole.yml.j2 create mode 100644 roles/lego/templates/lego-clusterolebinding.yml.j2 create mode 100644 roles/lego/templates/lego-configmap.yml.j2 create mode 100644 roles/lego/templates/lego-controller.yml.j2 create mode 100644 roles/lego/templates/lego-namespace.yml.j2 create mode 100644 roles/lego/templates/lego-sa.yml.j2 diff --git a/group_vars/all/vault b/group_vars/all/vault index 041f1e5..088ab18 100644 --- a/group_vars/all/vault +++ b/group_vars/all/vault @@ -1,86 +1,88 @@ $ANSIBLE_VAULT;1.1;AES256 -37663365346233373132393332613765346537636630326332376263613730303537306265386435 -3936383964313331373764623362326663353465633631340a663134343934386461623665316538 -61326266613537333930613231643635656335313964336632393566383231306232383966353061 -6232363866653333350a343336663830663138323337336239646630643465356631353334363663 -64386533353534393931626631363361356431656232636232643166353861346337653435623165 -34666439333738303336613833653664663766643865633361663738656466346465333836396235 -38386166666331393666643435333232356531636436333864626461613664646334356439363064 -64643864343032346437653363373531356538376433383832646365313664613534613430393032 -32303536623164313834323463396464343337316665316662656163623030383331353366343434 -63323031303561643030656161316130353738366439363139623461313665666233636362323363 -35303934623562323435383262653932386438313730376638353035613961323831386438653736 -64643538323438393737343235613631663831643761306566653038373934343965623831343831 -65663666636132326538613430666239303863336134316665346565316266326231353361396238 -30343632396135346238313639376631393233656534316332323330633335303233323466323331 -66383437376435613530373136383539646265313461633533613536623635646532396336393465 -38663231366636396636353861633965356162656334333031613138323233663339633439376231 -61323165646630303434623034366661666133336163663138666465613466643932336338333336 -37343364373464366634303836373636306133633831313835316236653630356330376535653439 -30363836646562613633626538633535643531333233643566356636373331633338646639373430 -39386138323766366461393534336334383138356330643730383163373634396235366564383637 -32353131626365393161346437323162323562343961643766613032333130323536316137393332 -63396235623631313934633337343430353563333933363339333964636334346437613033646234 -37636336316331346435353232386262336637346536623533353464333338613339336230353230 -65666536613961333334626239306437653364343230636232636536393962363134623230373262 -65326438313033396466353833316565653037316638336163363233393330323531353031663231 -35393765316432656432393932366133326334643337313264333762373037363463356661373539 -34623864336365303732383033383961333038663862303961663130643730396239333930366331 -37393031396533346633623364663530653937393934303463303137363734383031376538633561 -64666531333436626434613935663137646533656431323539323965646137626261653536383961 -36396130393633396631653034356530306565303034643233346436356530656665376433653936 -63353636616163313237633133666238663862336530306636616464356535333438383864316235 -37663238306132323434326235386361613834323832636431363561613464313164393339363537 -34313066366636613932373766353931393239303335343839343330396633656630316164326465 -33643337303064656638643934386365366539653666363834323434353738663537303265623438 -30663137653039383566353235323239653834643661303330336130303331316366316464353062 -61623463326533633533306638303661353334613662373763646234623535303633346664646538 -63643635356430326437613834623662316535666533346135623537376531316330353634323361 -31653138623136626135316564376636623530623938633134323561633037653834656363613634 -64633739363166343434383631653833336336363336386166386561613332623662386534656132 -64383939393762363133313438656530366463386135323634623634316132613663323866353031 -35346661313331383664333433386438303836376634303238366461616131376534636334633332 -65306665303631663462353838353763343330316430653037326137376530393766383531666666 -35643564626132383964653932383934346265323231376334353137396534663931336432336665 -34393138393763353034626234653837613463383137613033393839643437613032656232326634 -37623165353633316337396639636638636530656531366362613535626565393966336461613434 -32346635386462386431343237366463313833313262626436396637313865373166323164643131 -37613338336165646635383935333765373063623938626265323761363261643161366630303839 -35393132613231373263313332333064313638303762373561356630373433323230373633333434 -36376664336231653262636532353338303037323963643162393434303132613364353133313634 -32303465396337373961356635323733303538663436616435306338653936663733616236623261 -62313631326561343537353765636666373466613538613537636466333339623730323462363064 -38326662323530626365393837613539336134643935316334373432646234313836323663303532 -63356432333632646365653538663136666364653464656163323832373432303939656261356334 -31393634303638313465363636623464333964363066353035376638356663376331353633366663 -30333434333538323432356533643736616134303664373361626438616334333535363237313535 -64376261643638313562363464343235346166343939366639613564353035613964633931626634 -66313066623338323833356334336531356666626466623361653764663531663739343636376631 -64633230626231383234343532613233393361373563303836613332326636386361303135626439 -33636237653135316631626330633233656463626263343131366261383838363431643737386230 -33343931343264313632346463666433323437646439656433313961353461333337343461643434 -39356166616364663761356338383364363730633963326337353264343333636134633932613765 -61336166373266326336653733316530386163396436633036313831333533336435313264393339 -32363961313135633937613036643538303261393734666163623466613930366131306662313339 -37383334346636353966623666376136666236666564333466613462393031656331373532633262 -30323432343761313233623763333833663561363336613063366637306666373666646162323431 -34376163626537333137316539323934336364383130353264383837396635663536343761373733 -66396430633133343030313730633432343661386338313162323732323838373134393432333533 -64333738646135656437323134303038353130396266353136383561353066313432376231306335 -35613437373430636261313130663531346635323861316461396361313163396265323734623438 -62313061643835336136323561303866653130613365633163353031663365363566383662633333 -38356232316565653435653832333564643163326537393664316634363361653537333037643565 -62626164386439383862613937663632616533323634303532646663623331643066373030383237 -61363737313863663832363766313037653066316564356330663830336162323630373733383737 -61623933633962326133616165336632303938643735356439363730343261653661643535383762 -62616638623235613739653564313430656265376132386332666134633530336639313165613134 -35363566666434383062333835366131373366613861356362306235373664386461353864653863 -65663235623433316239366336636266343265326238313937346239623033326563396138306365 -39313361643463636665633531346137636265333833656534363666386339396334323831633035 -34363362373630323964323734343235303761663766336136316139353238363338656335373033 -36306436626335353834643231386263373361383431393864393235643135633033643065613031 -65386538306638323537366439346166646231666638616534613064333266666237323232303033 -38303030313162316338363064396633623365303562373233663433356263653033613234386231 -31376533666139313564666566316266613234623034386162653336666436643239666130663636 -39386265313562366538393839393232653736303736613232656139643236313132636337653430 -6366 +34396532356664616238616439303637376436383835356365663962623232653431333838353833 +6265333930636437313962616132623535373831363462340a323431346162633731626161386162 +37303239623761663639393130366563353961646261363032393437316434343237313164343137 +6330613465663433360a613864396335303430343133313164343133313632653430643439353632 +35363432383436356564393362333066303936616634303064376662663331643039386330333332 +30333563346237366165306464393532333738333336656537353237343835613865616137356132 +63366631623535343339666335336365393065616233633033343966396235326165383565616666 +31646437656438376263363337393331646466333231346332613863633865353564356631343362 +39353431326363646265666533643364653563376333626562376461623264343836306362616237 +34636632633130623131313463343730613265613232663131373334376466326133303630313863 +38643862653335633964393432656336366465393566623830313235373034373166386165636535 +32373637333735363936666135303531633265323831613738373466313536356439643139326666 +39613431323735376431616165663661626334353466353731373830343935666164633239653838 +30366331623662393264336238376434323065386235343933353364356536636165623231623162 +37393330663836653661643161633763626338626531643561303962383934336564373332393561 +33653935313437626361313661643934333934313432366232656338393732646366643965623135 +64343664323664323538373637306439613465343638366238333437326563323531396666353635 +38333934653332653336353330663763353534646261383261653934323963663038393135643565 +36343031343263323632353734646633343534613739316633393238316332623262633065623237 +32323832663761353332643331363835303039363231383264373733653935306361393634373837 +36343536333530626238383866653136343034346666313866366330633439663665306361383436 +36323833623136373038343435656465323835383236653039636436613461363437323162333433 +63643864363261666138303931613938663561653430666238643036643635373831333536646130 +37393532356435316136316239366538653331643663646662313032646133343562383663616131 +62653838666432376630313031353030633834396166393364393239316366626339633233613831 +35616363633131336137616436346232633963343332303464333764343462626232396336373033 +64623135306432343233306562376564376334366631353330623462303466326265363031626662 +33663131623964393531343036616531306336656561313832343865383061376566623330373962 +35643231333066666437653033623736356137343764366130383864323432386135383965333533 +35656637393239653234646538663434343331323435373838393138613739653362346364616336 +64663934396332313163656464656537343234653533386535343238376630636334306635613438 +65356161326666613830636566393864373766326664373363323765353032366435346264383263 +37346433346565636531316466303834636166636131396534316664303834386362633337666130 +32636538306464363064383036326363316630316266393239363332353733363263663139666432 +32626464383463386636363465643637613335326235376462353136356234613262333861373236 +39383935643633373161373437653338313764323536316461333466396138363962333936353133 +34613132363334373461353461336535333030343964363931363562333961333938633765363936 +38636162316333336536643138316332636536383865313632306334653561356331376162336332 +62356166373362313033663836373437313932393461383637393137363961373331626334396231 +37643564613633336234653135633231623063653431653933623230323265626236386338643631 +39326264313462373563353535376365623163396633333163633161323538666639333938303966 +38656362643437363265333032303231376631633462373730303235663232626231376438616266 +66383266656563643836323037393238666164363133653838333138663631336532346135613732 +32633765346364386365666163366263333461313535343837323764623237666166636166613730 +65636131636561356663383439616530633362303037393935323031353464376338366565643330 +66656139363461336632626364633930313139306263353434346662646339313739303762343261 +39633762363430383963346639656263303437343536336163636265636335653265333833373665 +30333462333566653837613832323430663364393535616533353038356136326366626562373736 +63646536666166313038326261303839393235303730613762313063373437393431373261393839 +32366230333139313138376662653765393336646532353534343437383330313237363636326239 +64383165653163366637316465396135666466353538636236313532363462623032373565653934 +34366238363938303630613635633164393030666333396166383963646165333237616261613030 +35633164343232663238633563353534343532346265646561306437616262616532333535333364 +38623934316537633164376639613564633036373334303131386166353737656131623066656162 +65656439666233376337653865663465303032616538363364363239336431653139313265376332 +39333131626336396261663530323335613839333833393333333665366266323535656633343465 +62636461373832393939323763626332323536353762376366616136396665633033346539353034 +33303465613434313166643431653836386466383732663630663138323466393963646331393962 +35366331353839353835363035383662366130313864353433383837663161336465376262636231 +66663038383039646561303235313930366263366365323863386335343730323534316666333237 +31653166333132353533343637656238383137346231623232303166373436373833643439303037 +34326237373866353632346465636636346463373364373461393266666434353434353536663463 +38343366646630333662393463373763636339383562313533343332623831306634313737663630 +66346638346366353537643436326538356661343638356334343739346438396434333330313066 +37656235323039356165663361336461306265303264363434656165363663613663643639383462 +61306334626336636134313066656463656363633862643564326330643435386433306232323863 +37633239633366336266366233343031656439343666656130653366336332663439356131653236 +64383761366239323038386535323933383466633864613066313230386535333363343830633234 +64373436363831663437353335316531306437633937666133643665653662326263373431616561 +66613230663136343330386231623634363763396365653734383938386364303064323933376639 +37376663393065363661353232313030666535643633366133303639343031353938393430353432 +62376165323737336132396361353037613832316562646663646539336132336236613336666431 +66363232366434663936386465313634313639636633346633636433663165366361613861323062 +62313836663364326365616139663066376531393135343933386236323165653334376265343238 +39323466643031396663356237626364653462396264316233323838363563356663316335323261 +61303335646265376261353330376165666462353635666338353036633533393764323831383536 +39363431373831643637333337656539636433646665376336336632653135326461316232613037 +36626465303439346635316536376163343638353639303636366163363633633965353033663964 +64363032656633303139316165323538386461376238313630653264643963366130626263303934 +33636335646663643666656133366536343162333036663134663034393735313830626339303139 +36343932323539393061643063613736353035633336613839616238386234653634356661663637 +32363439316662626430353435373030346465346339653733366539633566336537323433323665 +36366162363030623139383962343964336131373764636433323165353534323232383666353965 +65383865643538663561353666333430366165666263323432613736336565316338393935393838 +36356639363061663862373833613334666564343634616237346332376436626630373933383365 +36336564626334356432643132623466633364663631616638363136376464386465656438303231 +663039343938383434326237303934336564 diff --git a/group_vars/k8s b/group_vars/k8s index 4583e84..c1cf24f 100644 --- a/group_vars/k8s +++ b/group_vars/k8s @@ -2,6 +2,10 @@ ansible_user: root ansible_port: 22 +dashboard_subdomain: dashboard +scaleway_ipaddr: 51.158.77.6 +scaleway_reverse_ipaddr: k8s.banditlair.com + harden_linux_sshd_settings_user: "^Port ": "Port 22" harden_linux_ufw_rules: diff --git a/group_vars/k8s_proxy b/group_vars/k8s_proxy index 718ef37..88eb664 100644 --- a/group_vars/k8s_proxy +++ b/group_vars/k8s_proxy @@ -1,2 +1,3 @@ --- -vpn_ip: 192.168.66.{{ 0 +(inventory_hostname|regex_replace('\D+','')|int) }} \ No newline at end of file +vpn_ip: 192.168.66.{{ 0 +(inventory_hostname|regex_replace('\D+','')|int) }} +keepalived_ip: "192.168.66.254" \ No newline at end of file diff --git a/k8s.yml b/k8s.yml index 9ecca2e..6825fbe 100644 --- a/k8s.yml +++ b/k8s.yml @@ -15,56 +15,12 @@ roles: - role: kubernetes tags: kubernetes - - -#- hosts: localhost -# become: yes -# gather_facts: no -# roles: -# - role: harden-linux -# tags: role-harden-linux -# - role: githubixx.peervpn -# tags: role-peervpn -#- hosts: k8s -# vars: -# ansible_user: ubuntu -# gather_facts: no -# roles: -# - role: harden-linux -# tags: role-harden-linux -#- hosts: all -# become: yes -# roles: -# - role: peervpn -# tags: role-peervpn -#- hosts: k8s_ca -# become: yes -# gather_facts: no -# roles: -# - role: cfssl -# tags: role-cfssl -# - role: kubernetes-ca -# tags: role-kubernetes-ca -#- hosts: k8s_etcd -# become: yes -# gather_facts: no -# roles: -# - role: etcd -# tags: role-etcd -#- hosts: k8s_master -# gather_facts: no -# roles: -# - role: kubernetes-controller -# tags: role-kubernetes-controller -#- hosts: k8s_worker -# gather_facts: no -# roles: -# - role: githubixx.kubernetes-worker -# tags: role-kubernetes-worker -#- hosts: k8s -# gather_facts: no -# roles: -# - role: githubixx.flanneld -# tags: role-kubernetes-flanneld -# - role: githubixx.docker -# tags: role-docker +- hosts: k8s_masters:k8s_proxy + gather_facts: false + roles: + - role: ingress + tags: ingress + - role: lego + tags: lego + - role: kubernetes-dashboard + tags: dashboard \ No newline at end of file diff --git a/library/kube.py b/library/kube.py new file mode 100644 index 0000000..fa8312f --- /dev/null +++ b/library/kube.py @@ -0,0 +1,300 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +DOCUMENTATION = """ +--- +module: kube +short_description: Manage Kubernetes Cluster +description: + - Create, replace, remove, and stop resources within a Kubernetes Cluster +version_added: "2.0" +options: + name: + required: false + default: null + description: + - The name associated with resource + filename: + required: false + default: null + description: + - The path and filename of the resource(s) definition file. + kubectl: + required: false + default: null + description: + - The path to the kubectl bin + namespace: + required: false + default: null + description: + - The namespace associated with the resource(s) + resource: + required: false + default: null + description: + - The resource to perform an action on. pods (po), replicationControllers (rc), services (svc) + label: + required: false + default: null + description: + - The labels used to filter specific resources. + server: + required: false + default: null + description: + - The url for the API server that commands are executed against. + force: + required: false + default: false + description: + - A flag to indicate to force delete, replace, or stop. + all: + required: false + default: false + description: + - A flag to indicate delete all, stop all, or all namespaces when checking exists. + log_level: + required: false + default: 0 + description: + - Indicates the level of verbosity of logging by kubectl. + state: + required: false + choices: ['present', 'absent', 'latest', 'reloaded', 'stopped'] + default: present + description: + - present handles checking existence or creating if definition file provided, + absent handles deleting resource(s) based on other options, + latest handles creating or updating based on existence, + reloaded handles updating resource(s) definition using definition file, + stopped handles stopping resource(s) based on other options. +requirements: + - kubectl +author: "Kenny Jones (@kenjones-cisco)" +""" + +EXAMPLES = """ +- name: test nginx is present + kube: name=nginx resource=rc state=present + +- name: test nginx is stopped + kube: name=nginx resource=rc state=stopped + +- name: test nginx is absent + kube: name=nginx resource=rc state=absent + +- name: test nginx is present + kube: filename=/tmp/nginx.yml +""" + + +class KubeManager(object): + + def __init__(self, module): + + self.module = module + + self.kubectl = module.params.get('kubectl') + if self.kubectl is None: + self.kubectl = module.get_bin_path('kubectl', True) + self.base_cmd = [self.kubectl] + + if module.params.get('server'): + self.base_cmd.append('--server=' + module.params.get('server')) + + if module.params.get('log_level'): + self.base_cmd.append('--v=' + str(module.params.get('log_level'))) + + if module.params.get('namespace'): + self.base_cmd.append('--namespace=' + module.params.get('namespace')) + + self.all = module.params.get('all') + self.force = module.params.get('force') + self.name = module.params.get('name') + self.filename = module.params.get('filename') + self.resource = module.params.get('resource') + self.label = module.params.get('label') + + def _execute(self, cmd): + args = self.base_cmd + cmd + try: + rc, out, err = self.module.run_command(args) + if rc != 0: + self.module.fail_json( + msg='error running kubectl (%s) command (rc=%d): %s' % (' '.join(args), rc, out or err)) + except Exception as exc: + self.module.fail_json( + msg='error running kubectl (%s) command: %s' % (' '.join(args), str(exc))) + return out.splitlines() + + def _execute_nofail(self, cmd): + args = self.base_cmd + cmd + rc, out, err = self.module.run_command(args) + if rc != 0: + return None + return out.splitlines() + + def create(self, check=True, force=True): + if check and self.exists(): + return [] + + cmd = ['apply'] + + if force: + cmd.append('--force') + + if not self.filename: + self.module.fail_json(msg='filename required to create') + + cmd.append('--filename=' + self.filename) + + return self._execute(cmd) + + def replace(self, force=True): + + cmd = ['apply'] + + if force: + cmd.append('--force') + + if not self.filename: + self.module.fail_json(msg='filename required to reload') + + cmd.append('--filename=' + self.filename) + + return self._execute(cmd) + + def delete(self): + + if not self.force and not self.exists(): + return [] + + cmd = ['delete'] + + if self.filename: + cmd.append('--filename=' + self.filename) + else: + if not self.resource: + self.module.fail_json(msg='resource required to delete without filename') + + cmd.append(self.resource) + + if self.name: + cmd.append(self.name) + + if self.label: + cmd.append('--selector=' + self.label) + + if self.all: + cmd.append('--all') + + if self.force: + cmd.append('--ignore-not-found') + + return self._execute(cmd) + + def exists(self): + cmd = ['get'] + + if not self.resource: + return False + + cmd.append(self.resource) + + if self.name: + cmd.append(self.name) + + cmd.append('--no-headers') + + if self.label: + cmd.append('--selector=' + self.label) + + if self.all: + cmd.append('--all-namespaces') + + result = self._execute_nofail(cmd) + if not result: + return False + return True + + def stop(self): + + if not self.force and not self.exists(): + return [] + + cmd = ['stop'] + + if self.filename: + cmd.append('--filename=' + self.filename) + else: + if not self.resource: + self.module.fail_json(msg='resource required to stop without filename') + + cmd.append(self.resource) + + if self.name: + cmd.append(self.name) + + if self.label: + cmd.append('--selector=' + self.label) + + if self.all: + cmd.append('--all') + + if self.force: + cmd.append('--ignore-not-found') + + return self._execute(cmd) + + +def main(): + + module = AnsibleModule( + argument_spec=dict( + name=dict(), + filename=dict(), + namespace=dict(), + resource=dict(), + label=dict(), + server=dict(), + kubectl=dict(), + force=dict(default=False, type='bool'), + all=dict(default=False, type='bool'), + log_level=dict(default=0, type='int'), + state=dict(default='present', choices=['present', 'absent', 'latest', 'reloaded', 'stopped']), + ) + ) + + changed = False + + manager = KubeManager(module) + state = module.params.get('state') + if state == 'present': + result = manager.create(check=False) + + elif state == 'absent': + result = manager.delete() + + elif state == 'reloaded': + result = manager.replace() + + elif state == 'stopped': + result = manager.stop() + + elif state == 'latest': + result = manager.replace() + + else: + module.fail_json(msg='Unrecognized state %s.' % state) + + if result: + changed = True + module.exit_json(changed=changed, + msg='success: %s' % (' '.join(result)) + ) + + +from ansible.module_utils.basic import * # noqa +if __name__ == '__main__': + main() diff --git a/roles/ingress/defaults/main.yml b/roles/ingress/defaults/main.yml new file mode 100644 index 0000000..1d8709c --- /dev/null +++ b/roles/ingress/defaults/main.yml @@ -0,0 +1,10 @@ +replicas_default_backend: 1 +image_default_backend: gcr.io/google_containers/defaultbackend +version_default_backend: 1.4 +nginx_ingress_controller_image: gcr.io/google_containers/nginx-ingress-controller +nginx_ingress_controller_version: 0.9.0-beta.15 + +scaleway_servername1: proxy1 +scaleway_servername2: proxy2 +scaleway_ipaddr: "" # set this in the inventory file +scaleway_reverse_ipaddr: "" # set this in the inventory file diff --git a/roles/ingress/tasks/main.yml b/roles/ingress/tasks/main.yml new file mode 100644 index 0000000..d5f73ac --- /dev/null +++ b/roles/ingress/tasks/main.yml @@ -0,0 +1,97 @@ +--- +- name: nginx_ingress_controller | Getting node labels + command: "kubectl get nodes -l role=ingress-controller" + register: nodes + when: inventory_hostname == initial_master + +- name: nginx_ingress_controller | Printing nodes + debug: var=nodes + when: inventory_hostname == initial_master + +- name: nginx_ingress_controller | Labelling proxy nodes with role=ingress_controller + command: "kubectl label node {{ hostvars[item].ansible_hostname }} role=ingress-controller" + with_items: + - "{{ groups['k8s_proxy'] }}" + when: + - inventory_hostname == initial_master + - hostvars[item].ansible_hostname not in nodes.stdout + +- name: nginx_ingress_controller | Templating manifests + template: + src: "{{ item }}" + dest: "/tmp/{{ item | regex_replace('.j2', '') }}" + with_items: + - default-backend-controller.yml.j2 + - default-backend-service.yml.j2 + - nginx-ingress-clusterolebinding.yml.j2 + - nginx-ingress-configmap.yml.j2 + - nginx-ingress-sa.yml.j2 + - nginx-ingress-clusterole.yml.j2 + - nginx-ingress-controller.yml.j2 + - nginx-ingress-service.yml.j2 + when: inventory_hostname == initial_master + +- name: nginx_ingress_controller | Deploy the nginx_ingress_controller + kube: + name: "{{ item.name }}" + resource: "{{ item.type }}" + filename: "{{ item.file }}" + state: latest + with_items: + - { 'name': 'default-http-backend', 'type': 'deploy', 'file': '/tmp/default-backend-controller.yml' } + - { 'name': 'default-http-backend', 'type': 'svc', 'file': '/tmp/default-backend-service.yml' } + - { 'name': 'ingress', 'type': 'clusterrolebinding', 'file': '/tmp/nginx-ingress-clusterolebinding.yml' } + - { 'name': 'system:ingress', 'type': 'clusterrole', 'file': '/tmp/nginx-ingress-clusterole.yml' } + - { 'name': 'ingress', 'type': 'sa', 'file': '/tmp/nginx-ingress-sa.yml' } + - { 'name': 'nginx-ingress-cfg', 'type': 'configmap', 'file': '/tmp/nginx-ingress-configmap.yml' } + - { 'name': 'nginx-ingress-controller', 'type': 'deploy', 'file': '/tmp/nginx-ingress-controller.yml' } + - { 'name': 'nginx-ingress', 'type': 'svc', 'file': '/tmp/nginx-ingress-service.yml' } + when: inventory_hostname == initial_master + +- name: nginx_ingress_controller | Removing manifest + file: + path: "/tmp/{{ item }}" + state: absent + with_items: + - default-backend-controller.yml + - default-backend-service.yml + - nginx-ingress-clusterolebinding.yml + - nginx-ingress-configmap.yml + - nginx-ingress-sa.yml + - nginx-ingress-clusterole.yml + - nginx-ingress-controller.yml + - nginx-ingress-service.yml + when: inventory_hostname == initial_master + +- name: nginx_ingress_controller | Creating directory for scaleway-ipmove + file: + path: /usr/local/bin/scaleway-ipmove + state: directory + when: "'k8s_proxy' in group_names" + +- name: nginx_ingress_controller | Getting scaleway-ipmove.py + git: + repo: https://github.com/chmod666org/scaleway-ipmove + dest: /usr/local/bin/scaleway-ipmove + force: yes + when: "'k8s_proxy' in group_names" + +- name: nginx_ingress_controller | notify.sh + template: + src: notify.sh.j2 + dest: /usr/local/bin/scaleway-ipmove/notify.sh + mode: 0500 + owner: root + group: root + when: "'k8s_proxy' in group_names" + +# this runs keepalived on proxy nodes +- name: nginx_ingress_controller | Templating keepalived on proxy node + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + with_items: + - { 'src': 'keepalived.yml.j2', 'dest': '/etc/kubernetes/manifests/keepalived.yml' } + when: + - "'k8s_proxy' in group_names" + - groups.k8s_proxy|length > 1 diff --git a/roles/ingress/templates/default-backend-controller.yml.j2 b/roles/ingress/templates/default-backend-controller.yml.j2 new file mode 100644 index 0000000..c762b7a --- /dev/null +++ b/roles/ingress/templates/default-backend-controller.yml.j2 @@ -0,0 +1,39 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: default-http-backend + labels: + k8s-app: default-http-backend + namespace: kube-system +spec: + replicas: {{ replicas_default_backend }} + template: + metadata: + labels: + k8s-app: default-http-backend + spec: + terminationGracePeriodSeconds: 60 + containers: + - name: default-http-backend + # Any image is permissable as long as: + # 1. It serves a 404 page at / + # 2. It serves 200 on a /healthz endpoint + image: {{ image_default_backend }}:{{ version_default_backend }} + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + initialDelaySeconds: 30 + timeoutSeconds: 5 + ports: + - containerPort: 8080 + resources: + limits: + cpu: 10m + memory: 20Mi + requests: + cpu: 10m + memory: 20Mi + nodeSelector: + role: ingress-controller diff --git a/roles/ingress/templates/default-backend-service.yml.j2 b/roles/ingress/templates/default-backend-service.yml.j2 new file mode 100644 index 0000000..c03eb98 --- /dev/null +++ b/roles/ingress/templates/default-backend-service.yml.j2 @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: default-http-backend + namespace: kube-system + labels: + k8s-app: default-http-backend +spec: + ports: + - port: 80 + targetPort: 8080 + selector: + k8s-app: default-http-backend diff --git a/roles/ingress/templates/keepalived.yml.j2 b/roles/ingress/templates/keepalived.yml.j2 new file mode 100644 index 0000000..6a7aebb --- /dev/null +++ b/roles/ingress/templates/keepalived.yml.j2 @@ -0,0 +1,37 @@ +apiVersion: v1 +kind: Pod +metadata: + name: keepalived + namespace: kube-system +spec: + hostNetwork: true + volumes: + - hostPath: + path: /usr/local/bin/scaleway-ipmove/ + name: scaleway-moveip + containers: + - name: keepalived + image: chmod666/keepalived:latest + # if tag is latest imagePullPolicy is always + # but when keepalived is backup a proxy may have no connection to the internet + # to avoid keepalived not starting in that case, we're putting imagePullPolicy: IfNotPresent + # assuming the image was already be pulled at cluster creation. Neat. + imagePullPolicy: IfNotPresent + volumeMounts: + - mountPath: "/mnt" + name: scaleway-moveip + securityContext: + capabilities: + add: + - NET_ADMIN + env: + - name: KEEPALIVED_INTERFACE + value: tun0 + - name: KEEPALIVED_UNICAST_PEERS + value: "#PYTHON2BASH:['{{ groups['k8s_proxy'] | map('extract', hostvars, ['vpn_ip']) | join("', '") }}']" + - name: KEEPALIVED_VIRTUAL_IPS + value: "#PYTHON2BASH:['{{ keepalived_ip }}']" + - name: KEEPALIVED_PRIORITY + value: "{{ groups['k8s_proxy'].index(inventory_hostname) + 1 }}" + - name: KEEPALIVED_NOTIFY + value: "/mnt/notify.sh" diff --git a/roles/ingress/templates/nginx-ingress-clusterole.yml.j2 b/roles/ingress/templates/nginx-ingress-clusterole.yml.j2 new file mode 100644 index 0000000..8832c83 --- /dev/null +++ b/roles/ingress/templates/nginx-ingress-clusterole.yml.j2 @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: system:ingress +rules: +- apiGroups: + - "" + resources: ["configmaps","secrets","endpoints","events","services"] + verbs: ["list","watch","create","update","delete","get"] +- apiGroups: + - "" + - "extensions" + resources: ["services","nodes","ingresses","pods","ingresses/status"] + verbs: ["list","watch","create","update","delete","get"] diff --git a/roles/ingress/templates/nginx-ingress-clusterolebinding.yml.j2 b/roles/ingress/templates/nginx-ingress-clusterolebinding.yml.j2 new file mode 100644 index 0000000..77c7039 --- /dev/null +++ b/roles/ingress/templates/nginx-ingress-clusterolebinding.yml.j2 @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: ingress +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:ingress +subjects: + - kind: ServiceAccount + name: ingress + namespace: kube-system diff --git a/roles/ingress/templates/nginx-ingress-configmap.yml.j2 b/roles/ingress/templates/nginx-ingress-configmap.yml.j2 new file mode 100644 index 0000000..06f15cc --- /dev/null +++ b/roles/ingress/templates/nginx-ingress-configmap.yml.j2 @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-ingress-cfg + namespace: kube-system + labels: + app: nginx-ingress-cfg +data: + enable-sticky-sessions: 'true' ## use ROUTE cookie to provide session affinity + enable-vts-status: 'true' ## Allows the replacement of the default status page nginx-module-vts diff --git a/roles/ingress/templates/nginx-ingress-controller.yml.j2 b/roles/ingress/templates/nginx-ingress-controller.yml.j2 new file mode 100644 index 0000000..dbcf089 --- /dev/null +++ b/roles/ingress/templates/nginx-ingress-controller.yml.j2 @@ -0,0 +1,66 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: nginx-ingress-controller + labels: + k8s-app: nginx-ingress-controller + namespace: kube-system +spec: + # on replica per proxy + replicas: {{ groups['k8s_proxy'] | length }} + template: + metadata: + labels: + k8s-app: nginx-ingress-controller + annotations: + prometheus.io/port: '10254' + prometheus.io/scrape: 'true' + spec: + # hostNetwork makes it possible to use ipv6 and to preserve the source IP correctly regardless of docker configuration + # however, it is not a hard dependency of the nginx-ingress-controller itself and it may cause issues if port 10254 already is taken on the host + # that said, since hostPort is broken on CNI (https://github.com/kubernetes/kubernetes/issues/31307) we have to use hostNetwork where CNI is used + # like with kubeadm + # hostNetwork: true + serviceAccountName: ingress + terminationGracePeriodSeconds: 60 + #https://github.com/kubernetes/contrib/issues/2135 + # CNI and hostPort does not work using hostNetwork + hostNetwork: true + containers: + - image: {{ nginx_ingress_controller_image }}:{{ nginx_ingress_controller_version }} + name: nginx-ingress-controller + readinessProbe: + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + livenessProbe: + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + timeoutSeconds: 1 + ports: + - containerPort: 80 + #hostPort: 80 + - containerPort: 443 + #hostPort: 443 + - containerPort: 18080 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + args: + - /nginx-ingress-controller + - --default-backend-service=$(POD_NAMESPACE)/default-http-backend + - --configmap=$(POD_NAMESPACE)/nginx-ingress-cfg + nodeSelector: + # node must be labelled with roles=ingress-controller + role: ingress-controller + diff --git a/roles/ingress/templates/nginx-ingress-sa.yml.j2 b/roles/ingress/templates/nginx-ingress-sa.yml.j2 new file mode 100644 index 0000000..f544f65 --- /dev/null +++ b/roles/ingress/templates/nginx-ingress-sa.yml.j2 @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ingress + namespace: kube-system diff --git a/roles/ingress/templates/nginx-ingress-service.yml.j2 b/roles/ingress/templates/nginx-ingress-service.yml.j2 new file mode 100644 index 0000000..97ba031 --- /dev/null +++ b/roles/ingress/templates/nginx-ingress-service.yml.j2 @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: nginx-ingress + namespace: kube-system +spec: + ports: + - port: 80 + name: http + - port: 443 + name: https + - port: 18080 + name: http-mgmt + selector: + k8s-app: nginx-ingress-controller diff --git a/roles/ingress/templates/notify.sh.j2 b/roles/ingress/templates/notify.sh.j2 new file mode 100644 index 0000000..466e9a3 --- /dev/null +++ b/roles/ingress/templates/notify.sh.j2 @@ -0,0 +1,35 @@ +#!/bin/bash + +# for ANY state transition. +# "notify" script is called AFTER the +# notify_* script(s) and is executed +# with 3 arguments provided by keepalived +# (ie don't include parameters in the notify line). +# arguments +# $1 = "GROUP"|"INSTANCE" +# $2 = name of group or instance +# $3 = target state of transition +# ("MASTER"|"BACKUP"|"FAULT") + +TYPE=$1 +NAME=$2 +STATE=$3 + +case $STATE in + "MASTER") echo "I'm the MASTER! Whup whup." > /proc/1/fd/1 + echo "Here is the master" + # this put the public ip on the master using the scaleway api + /mnt/scaleway-ipmove.py {{ scaleway_token }} {{ scaleway_servername1 }} {{ scaleway_servername2 }} {{ scaleway_ipaddr }} {{ scaleway_reverse_ipaddr }} {{ scaleway_orga }} + exit 0 + ;; + "BACKUP") echo "Ok, i'm just a backup, great." > /proc/1/fd/1 + echo "Here is the backup" + exit 0 + ;; + "FAULT") echo "Fault, what ?" > /proc/1/fd/1 + exit 0 + ;; + *) echo "Unknown state" > /proc/1/fd/1 + exit 1 + ;; +esac diff --git a/roles/kubernetes-dashboard/defaults/main.yml b/roles/kubernetes-dashboard/defaults/main.yml new file mode 100644 index 0000000..0d8f857 --- /dev/null +++ b/roles/kubernetes-dashboard/defaults/main.yml @@ -0,0 +1,15 @@ +--- +# set basic_auth_user as non-empty to enforce basic auth +basic_auth_user: "" +basic_auth_password: "" + +# e.g. the fqdn would be k8s.yourdomain.tld if +# dashboard_subdomain=k8s +# scaleway_reverse_ipaddr=yourdomain.tld +dashboard_subdomain: k8s + +dashboard_image: gcr.io/google_containers/kubernetes-dashboard-amd64 +dashboard_version: v1.10.0 + +init_dashboard_image: gcr.io/google_containers/kubernetes-dashboard-init-amd64 +init_dashboard_version: v1.0.1 diff --git a/roles/kubernetes-dashboard/files/dashboard-clusterrolebinding.yml b/roles/kubernetes-dashboard/files/dashboard-clusterrolebinding.yml new file mode 100644 index 0000000..70a11bb --- /dev/null +++ b/roles/kubernetes-dashboard/files/dashboard-clusterrolebinding.yml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: kubernetes-dashboard-crb +roleRef: + apiGroup: "" + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: kubernetes-dashboard + namespace: kube-system diff --git a/roles/kubernetes-dashboard/files/dashboard-role.yml b/roles/kubernetes-dashboard/files/dashboard-role.yml new file mode 100644 index 0000000..7be599d --- /dev/null +++ b/roles/kubernetes-dashboard/files/dashboard-role.yml @@ -0,0 +1,20 @@ +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: kubernetes-dashboard-minimal + namespace: kube-system +rules: + # Allow Dashboard to create and watch for changes of 'kubernetes-dashboard-key-holder' secret. +- apiGroups: [""] + resources: ["secrets"] + verbs: ["create", "watch"] +- apiGroups: [""] + resources: ["secrets"] + # Allow Dashboard to get, update and delete 'kubernetes-dashboard-key-holder' secret. + resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs"] + verbs: ["get", "update", "delete"] + # Allow Dashboard to get metrics from heapster. +- apiGroups: [""] + resources: ["services"] + resourceNames: ["heapster"] + verbs: ["proxy"] diff --git a/roles/kubernetes-dashboard/files/dashboard-rolebinding.yml b/roles/kubernetes-dashboard/files/dashboard-rolebinding.yml new file mode 100644 index 0000000..3c25842 --- /dev/null +++ b/roles/kubernetes-dashboard/files/dashboard-rolebinding.yml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: kubernetes-dashboard-minimal + namespace: kube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: kubernetes-dashboard-minimal +subjects: +- kind: ServiceAccount + name: kubernetes-dashboard + namespace: kube-system diff --git a/roles/kubernetes-dashboard/files/dashboard-sa.yml b/roles/kubernetes-dashboard/files/dashboard-sa.yml new file mode 100644 index 0000000..c13b050 --- /dev/null +++ b/roles/kubernetes-dashboard/files/dashboard-sa.yml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + k8s-app: kubernetes-dashboard + name: kubernetes-dashboard + namespace: kube-system diff --git a/roles/kubernetes-dashboard/files/dashboard-secret.yml b/roles/kubernetes-dashboard/files/dashboard-secret.yml new file mode 100644 index 0000000..278bbc1 --- /dev/null +++ b/roles/kubernetes-dashboard/files/dashboard-secret.yml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + labels: + k8s-app: kubernetes-dashboard + name: kubernetes-dashboard-certs + namespace: kube-system +type: Opaque diff --git a/roles/kubernetes-dashboard/files/dashboard-service.yml b/roles/kubernetes-dashboard/files/dashboard-service.yml new file mode 100644 index 0000000..760bc72 --- /dev/null +++ b/roles/kubernetes-dashboard/files/dashboard-service.yml @@ -0,0 +1,13 @@ +kind: Service +apiVersion: v1 +metadata: + labels: + k8s-app: kubernetes-dashboard + name: kubernetes-dashboard + namespace: kube-system +spec: + ports: + - port: 80 + targetPort: 9090 + selector: + k8s-app: kubernetes-dashboard diff --git a/roles/kubernetes-dashboard/files/heapster-clusterrolebinding.yml b/roles/kubernetes-dashboard/files/heapster-clusterrolebinding.yml new file mode 100644 index 0000000..6f407d8 --- /dev/null +++ b/roles/kubernetes-dashboard/files/heapster-clusterrolebinding.yml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: heapster + labels: + k8s-addon: monitoring-standalone.addons.k8s.io + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:heapster +subjects: +- kind: ServiceAccount + name: heapster + namespace: kube-system diff --git a/roles/kubernetes-dashboard/files/heapster-deployment.yml b/roles/kubernetes-dashboard/files/heapster-deployment.yml new file mode 100644 index 0000000..cd34283 --- /dev/null +++ b/roles/kubernetes-dashboard/files/heapster-deployment.yml @@ -0,0 +1,77 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: heapster + namespace: kube-system + labels: + k8s-addon: monitoring-standalone.addons.k8s.io + k8s-app: heapster + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile + version: v1.7.0 +spec: + replicas: 1 + selector: + matchLabels: + k8s-app: heapster + version: v1.7.0 + template: + metadata: + labels: + k8s-app: heapster + version: v1.7.0 + annotations: + scheduler.alpha.kubernetes.io/critical-pod: '' + spec: + serviceAccountName: heapster + containers: + - image: gcr.io/google_containers/heapster:v1.4.0 + name: heapster + livenessProbe: + httpGet: + path: /healthz + port: 8082 + scheme: HTTP + initialDelaySeconds: 180 + timeoutSeconds: 5 + resources: + # keep request = limit to keep this container in guaranteed class + limits: + cpu: 100m + memory: 300Mi + requests: + cpu: 100m + memory: 300Mi + command: + - /heapster + - --source=kubernetes.summary_api:'' + - image: gcr.io/google_containers/addon-resizer:2.0 + name: heapster-nanny + resources: + limits: + cpu: 50m + memory: 100Mi + requests: + cpu: 50m + memory: 100Mi + env: + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + command: + - /pod_nanny + - --cpu=80m + - --extra-cpu=0.5m + - --memory=140Mi + - --extra-memory=4Mi + - --deployment=heapster + - --container=heapster + - --poll-period=300000 + tolerations: + - key: "CriticalAddonsOnly" + operator: "Exists" diff --git a/roles/kubernetes-dashboard/files/heapster-role.yml b/roles/kubernetes-dashboard/files/heapster-role.yml new file mode 100644 index 0000000..5c62e0e --- /dev/null +++ b/roles/kubernetes-dashboard/files/heapster-role.yml @@ -0,0 +1,24 @@ +# Heapster's pod_nanny monitors the heapster deployment & its pod(s), and scales +# the resources of the deployment if necessary. +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: Role +metadata: + name: system:pod-nanny + namespace: kube-system + labels: + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile +rules: +- apiGroups: + - "" + resources: + - pods + verbs: + - get +- apiGroups: + - "extensions" + resources: + - deployments + verbs: + - get + - update diff --git a/roles/kubernetes-dashboard/files/heapster-rolebinding.yml b/roles/kubernetes-dashboard/files/heapster-rolebinding.yml new file mode 100644 index 0000000..c1d848d --- /dev/null +++ b/roles/kubernetes-dashboard/files/heapster-rolebinding.yml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: heapster-binding + namespace: kube-system + labels: + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: system:pod-nanny +subjects: +- kind: ServiceAccount + name: heapster + namespace: kube-system diff --git a/roles/kubernetes-dashboard/files/heapster-sa.yml b/roles/kubernetes-dashboard/files/heapster-sa.yml new file mode 100644 index 0000000..ee140b3 --- /dev/null +++ b/roles/kubernetes-dashboard/files/heapster-sa.yml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: heapster + namespace: kube-system + labels: + k8s-addon: monitoring-standalone.addons.k8s.io diff --git a/roles/kubernetes-dashboard/files/heapster-service.yml b/roles/kubernetes-dashboard/files/heapster-service.yml new file mode 100644 index 0000000..439801f --- /dev/null +++ b/roles/kubernetes-dashboard/files/heapster-service.yml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: heapster + namespace: kube-system + labels: + k8s-addon: monitoring-standalone.addons.k8s.io + kubernetes.io/name: "Heapster" + kubernetes.io/cluster-service: "true" +spec: + ports: + - port: 80 + targetPort: 8082 + selector: + k8s-app: heapster diff --git a/roles/kubernetes-dashboard/tasks/main.yml b/roles/kubernetes-dashboard/tasks/main.yml new file mode 100644 index 0000000..231b8d5 --- /dev/null +++ b/roles/kubernetes-dashboard/tasks/main.yml @@ -0,0 +1,105 @@ +--- +- block: + - name: Installing python-passlib + apt: + name: python-passlib + state: latest + register: result + retries: 3 + until: result is success + + - name: Creating htpasswd file if k8s has basic auth + htpasswd: + path: /tmp/auth + name: "{{ basic_auth_user }}" + password: "{{ basic_auth_password }}" + when: inventory_hostname == initial_master + + - name: Getting secrets + command: kubectl get secrets --namespace=kube-system + register: secrets + when: inventory_hostname == initial_master + + - name: Creating secret + command: kubectl create secret generic dashboard-basic-auth --namespace=kube-system --from-file=/tmp/auth + when: + - inventory_hostname == initial_master + - '"dashboard-basic-auth" not in secrets.stdout' + + - name: Deleting basic_auth file + file: + path: /tmp/auth + state: absent + when: inventory_hostname == initial_master + + when: basic_auth_user | length > 0 + +- name: Templating manifests + template: + src: "{{ item }}" + dest: "/tmp/{{ item | regex_replace('.j2', '') }}" + with_items: + - dashboard-ingress.yml.j2 + - dashboard-deployment.yml.j2 + when: inventory_hostname == initial_master + +- name: Copying manifests files + copy: + src: "{{ item }}" + dest: "/tmp/{{ item }}" + with_items: + - dashboard-rolebinding.yml + - dashboard-role.yml + - dashboard-sa.yml + - dashboard-clusterrolebinding.yml + - dashboard-secret.yml + - dashboard-service.yml + - heapster-rolebinding.yml + - heapster-clusterrolebinding.yml + - heapster-role.yml + - heapster-sa.yml + - heapster-service.yml + - heapster-deployment.yml + when: inventory_hostname == initial_master + +- name: Deploying kubernetes-dashboard + kube: + name: "{{ item.name }}" + resource: "{{ item.type }}" + filename: "{{ item.file }}" + state: latest + with_items: + - { 'name': 'kubernetes-dashboard', 'type': 'sa', 'file': '/tmp/dashboard-sa.yml' } + - { 'name': 'kubernetes-dashboard', 'type': 'clusterrolebinding', 'file': '/tmp/dashboard-clusterrolebinding.yml' } + - { 'name': 'kubernetes-dashboard', 'type': 'secret', 'file': '/tmp/dashboard-secret.yml' } + - { 'name': 'kubernetes-dashboard', 'type': 'service', 'file': '/tmp/dashboard-service.yml' } + - { 'name': 'kubernetes-dashboard', 'type': 'deployment', 'file': '/tmp/dashboard-deployment.yml' } + - { 'name': 'kubernetes-dashboard', 'type': 'ingress', 'file': '/tmp/dashboard-ingress.yml' } + - { 'name': 'heapster', 'type': 'sa', 'file': '/tmp/heapster-sa.yml' } + - { 'name': 'heapster', 'type': 'clusterrolebinding', 'file': '/tmp/heapster-clusterrolebinding.yml' } + - { 'name': 'heapster', 'type': 'rolebinding', 'file': '/tmp/heapster-rolebinding.yml' } + - { 'name': 'heapster', 'type': 'role', 'file': '/tmp/heapster-role.yml' } + - { 'name': 'heapster', 'type': 'service', 'file': '/tmp/heapster-service.yml' } + - { 'name': 'heapster', 'type': 'deployment', 'file': '/tmp/heapster-deployment.yml' } + when: inventory_hostname == initial_master + +- name: Removing manifest + file: + path: "/tmp/{{ item }}" + state: absent + with_items: + - dashboard-ingress.yml.j2 + - dashboard-deployment.yml.j2 + - dashboard-clusterrolebinding.yml + - dashboard-rolebinding.yml + - dashboard-role.yml + - dashboard-sa.yml + - dashboard-secret.yml + - dashboard-service.yml + - heapster-rolebinding.yml + - heapster-clusterrolebinding.yml + - heapster-role.yml + - heapster-sa.yml + - heapster-service.yml + - heapster-deployment.yml + when: inventory_hostname == initial_master diff --git a/roles/kubernetes-dashboard/templates/dashboard-deployment.yml.j2 b/roles/kubernetes-dashboard/templates/dashboard-deployment.yml.j2 new file mode 100644 index 0000000..8017bf2 --- /dev/null +++ b/roles/kubernetes-dashboard/templates/dashboard-deployment.yml.j2 @@ -0,0 +1,47 @@ +kind: Deployment +apiVersion: extensions/v1beta1 +metadata: + labels: + k8s-app: kubernetes-dashboard + name: kubernetes-dashboard + namespace: kube-system +spec: + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + k8s-app: kubernetes-dashboard + template: + metadata: + labels: + k8s-app: kubernetes-dashboard + spec: + containers: + - name: kubernetes-dashboard + image: {{ dashboard_image }}:{{ dashboard_version }} + ports: + - containerPort: 9090 + protocol: TCP + args: + # Uncomment the following line to manually specify Kubernetes API server Host + # If not specified, Dashboard will attempt to auto discover the API server and connect + # to it. Uncomment only if the default does not work. + # - --apiserver-host=http://my-address:port + volumeMounts: + # Create on-disk volume to store exec logs + - mountPath: /tmp + name: tmp-volume + livenessProbe: + httpGet: + path: / + port: 9090 + initialDelaySeconds: 30 + timeoutSeconds: 30 + volumes: + - name: tmp-volume + emptyDir: {} + serviceAccountName: kubernetes-dashboard + # Comment the following tolerations if Dashboard must not be deployed on master + tolerations: + - key: node-role.kubernetes.io/master + effect: NoSchedule diff --git a/roles/kubernetes-dashboard/templates/dashboard-ingress.yml.j2 b/roles/kubernetes-dashboard/templates/dashboard-ingress.yml.j2 new file mode 100644 index 0000000..1300a1a --- /dev/null +++ b/roles/kubernetes-dashboard/templates/dashboard-ingress.yml.j2 @@ -0,0 +1,30 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: kube-system + name: kubernetes-dashboard + annotations: + # enable kube-lego for this ingress + kubernetes.io/tls-acme: "true" + {% if basic_auth_user | length > 0 %} +ingress.kubernetes.io/auth-type: basic + # name of the secret that contains the user/password definitions + ingress.kubernetes.io/auth-secret: dashboard-basic-auth + # message to display with an appropiate context why the authentication is required + ingress.kubernetes.io/auth-realm: "Authentication is required to access the k8s dashboard " + {% endif %} + +spec: + # this enables tls for the specified domain names + tls: + - hosts: + - {{ dashboard_subdomain }}.{{ scaleway_reverse_ipaddr }} + secretName: dashboard-tls + rules: + - host: {{ dashboard_subdomain }}.{{ scaleway_reverse_ipaddr }} + http: + paths: + - path: / + backend: + serviceName: kubernetes-dashboard + servicePort: 80 diff --git a/roles/kubernetes/tasks/main.yml b/roles/kubernetes/tasks/main.yml index 8ef7918..174d415 100644 --- a/roles/kubernetes/tasks/main.yml +++ b/roles/kubernetes/tasks/main.yml @@ -24,7 +24,7 @@ shell: "ping {{ api_floating_ip }} -c 1" register: result changed_when: no - failed_when: ('100.0% packet loss' in result.stdout) + failed_when: ('100% packet loss' in result.stdout) - include: packages.yml diff --git a/roles/lego/defaults/main.yml b/roles/lego/defaults/main.yml new file mode 100644 index 0000000..143607b --- /dev/null +++ b/roles/lego/defaults/main.yml @@ -0,0 +1,4 @@ +--- +lego_email: deckard@chmod666.org +lego_image: jetstack/kube-lego +lego_version: 0.1.5 diff --git a/roles/lego/tasks/main.yml b/roles/lego/tasks/main.yml new file mode 100644 index 0000000..b1888ca --- /dev/null +++ b/roles/lego/tasks/main.yml @@ -0,0 +1,40 @@ +--- +- name: kube_lego | Templating manifests + template: + src: "{{ item }}" + dest: "/tmp/{{ item | regex_replace('.j2', '') }}" + with_items: + - lego-sa.yml.j2 + - lego-clusterolebinding.yml.j2 + - lego-clusterole.yml.j2 + - lego-configmap.yml.j2 + - lego-controller.yml.j2 + when: inventory_hostname == initial_master + +- name: kube_lego | Deploying kube-lego + kube: + name: "{{ item.name }}" + resource: "{{ item.type }}" + filename: "{{ item.file }}" + state: latest + with_items: + - { 'name': 'kube-lego', 'type': 'sa', 'file': '/tmp/lego-sa.yml' } + - { 'name': 'kube-lego', 'type': 'clusterrolebingind', 'file': '/tmp/lego-clusterolebinding.yml' } + - { 'name': 'kube-lego', 'type': 'clusterrole', 'file': '/tmp/lego-clusterole.yml' } + - { 'name': 'kube-lego', 'type': 'configmap', 'file': '/tmp/lego-configmap.yml' } + - { 'name': 'kube-lego', 'type': 'deploy', 'file': '/tmp/lego-controller.yml' } + when: inventory_hostname == initial_master + +- name: kube_lego | Removing manifest + file: + path: "/tmp/{{ item }}" + state: absent + with_items: + - lego-namespace.yml + - lego-sa.yml + - lego-clusterolebinding.yml + - lego-clusterole.yml + - lego-configmap.yml + - lego-controller.yml + when: inventory_hostname == initial_master + diff --git a/roles/lego/templates/lego-clusterole.yml.j2 b/roles/lego/templates/lego-clusterole.yml.j2 new file mode 100644 index 0000000..63d3bf3 --- /dev/null +++ b/roles/lego/templates/lego-clusterole.yml.j2 @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: system:kube-lego +rules: +- apiGroups: + - "" + resources: ["configmaps","secrets","endpoints","events","services"] + verbs: ["list","watch","create","update","delete","get"] +- apiGroups: + - "" + - "extensions" + resources: ["services","nodes","ingresses","pods","ingresses/status"] + verbs: ["list","watch","create","update","delete","get"] diff --git a/roles/lego/templates/lego-clusterolebinding.yml.j2 b/roles/lego/templates/lego-clusterolebinding.yml.j2 new file mode 100644 index 0000000..bb0ce13 --- /dev/null +++ b/roles/lego/templates/lego-clusterolebinding.yml.j2 @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: kube-lego +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:kube-lego +subjects: + - kind: ServiceAccount + name: kube-lego + namespace: kube-system diff --git a/roles/lego/templates/lego-configmap.yml.j2 b/roles/lego/templates/lego-configmap.yml.j2 new file mode 100644 index 0000000..22e7396 --- /dev/null +++ b/roles/lego/templates/lego-configmap.yml.j2 @@ -0,0 +1,10 @@ +apiVersion: v1 +metadata: + name: kube-lego + namespace: kube-system +data: + # modify this to specify your address + lego.email: "{{ lego_email }}" + # configure letsencrypt's production api + lego.url: "https://acme-v01.api.letsencrypt.org/directory" +kind: ConfigMap diff --git a/roles/lego/templates/lego-controller.yml.j2 b/roles/lego/templates/lego-controller.yml.j2 new file mode 100644 index 0000000..caecfa7 --- /dev/null +++ b/roles/lego/templates/lego-controller.yml.j2 @@ -0,0 +1,48 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: kube-lego + namespace: kube-system +spec: + replicas: 1 + template: + metadata: + labels: + app: kube-lego + spec: + serviceAccountName: kube-lego + containers: + - name: kube-lego + image: "{{ lego_image }}:{{ lego_version }}" + imagePullPolicy: Always + ports: + - containerPort: 8080 + env: + - name: LEGO_EMAIL + valueFrom: + configMapKeyRef: + name: kube-lego + key: lego.email + - name: LEGO_URL + valueFrom: + configMapKeyRef: + name: kube-lego + key: lego.url + - name: LEGO_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: LEGO_POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + readinessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 5 + timeoutSeconds: 1 + nodeSelector: + # node must be labelled with roles=ingress-controller + role: ingress-controller + diff --git a/roles/lego/templates/lego-namespace.yml.j2 b/roles/lego/templates/lego-namespace.yml.j2 new file mode 100644 index 0000000..be95521 --- /dev/null +++ b/roles/lego/templates/lego-namespace.yml.j2 @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: kube-lego diff --git a/roles/lego/templates/lego-sa.yml.j2 b/roles/lego/templates/lego-sa.yml.j2 new file mode 100644 index 0000000..bb53f17 --- /dev/null +++ b/roles/lego/templates/lego-sa.yml.j2 @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kube-lego + namespace: kube-system diff --git a/scripts/scw_inventory.py b/scripts/scw_inventory.py index 0bf1814..8f8627f 100755 --- a/scripts/scw_inventory.py +++ b/scripts/scw_inventory.py @@ -21,7 +21,7 @@ class SCWInventory(object): """ response: Dict[str, Any] - + def __init__(self): self.inventory = None self.auth_token = None @@ -78,7 +78,8 @@ class SCWInventory(object): for host, variables in self.response['_meta']['hostvars'].items(): if host != 'proxy1': variables['ansible_ssh_common_args'] = '-o ProxyCommand="ssh -W %h:%p -q root@' + \ - self.response['_meta']['hostvars']['proxy1']['public_ip'] + '"' + self.response['_meta']['hostvars']['proxy1'][ + 'public_ip'] + ' -o StrictHostKeyChecking=no"' def _add_to_response(self, group, hostname): """