From 431f1f43842c506ea8e469882d8b7256589ece00 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Tue, 2 Jun 2015 11:40:39 -0700 Subject: [PATCH] Add documentation for HA Kubernetes --- docs/high-availability.md | 203 ++++++++++++++++++ docs/high-availability/etcd.manifest | 104 +++++++++ docs/high-availability/ha.png | Bin 0 -> 38814 bytes docs/high-availability/ha.svg | 4 + .../high-availability/kube-apiserver.manifest | 103 +++++++++ .../kube-controller-manager.manifest | 100 +++++++++ .../high-availability/kube-scheduler.manifest | 39 ++++ docs/high-availability/podmaster.manifest | 57 +++++ 8 files changed, 610 insertions(+) create mode 100644 docs/high-availability.md create mode 100644 docs/high-availability/etcd.manifest create mode 100644 docs/high-availability/ha.png create mode 100644 docs/high-availability/ha.svg create mode 100644 docs/high-availability/kube-apiserver.manifest create mode 100644 docs/high-availability/kube-controller-manager.manifest create mode 100644 docs/high-availability/kube-scheduler.manifest create mode 100644 docs/high-availability/podmaster.manifest diff --git a/docs/high-availability.md b/docs/high-availability.md new file mode 100644 index 0000000000000..a1ffbd0598c8d --- /dev/null +++ b/docs/high-availability.md @@ -0,0 +1,203 @@ +# High Availability Kubernetes Clusters + +## Introduction +This document describes how to build a high-availability (HA) Kubernetes cluster. This is a fairly advanced topic. +Users who merely want to experiment with Kubernetes are encouraged to use configurations that are simpler to set up such as +the simple [Docker based single node cluster instructions](https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/getting-started-guides/docker.md), +or try [Google Container Engine](https://cloud.google.com/container-engine/) for hosted Kubernetes. + +Also, at this time high availability support for Kubernetes is not continuously tested in our end-to-end (e2e) testing. We will +be working to add this continuous testing, but for now the single-node master installations are more heavily tested. + +## Overview +Setting up a truly reliable, highly available distributed system requires a number of steps, it is akin to +wearing underwear, pants, a belt, suspenders, another pair of underwear, and another pair of pants. We go into each +of these steps in detail, but a summary is given here to help guide and orient the user. + +The steps involved are as follows: + * [Creating the reliable constituent nodes that collectively form our HA master implementation.](#reliable-nodes) + * [Setting up a redundant, reliable storage layer with clustered etcd.](#establishing-a-redundant-reliable-data-storage-layer) + * [Starting replicated, load balanced Kubernetes API servers](#replicated-api-servers) + * [Setting up master-elected Kubernetes scheduler and controller-manager daemons](#master-elected-components) + +Here's what the system should look like when it's finished: +![High availability Kubernetes diagram](high-availability/ha.png) + +Ready? Let's get started. + +## Initial set-up +The remainder of this guide assumes that you are setting up a 3-node clustered master, where each machine is running some flavor of Linux. +Examples in the guide are given for Debian distributions, but they should be easily adaptable to other distributions. +Likewise, this set up should work whether you are running in a public or private cloud provider, or if you are running +on bare metal. + +The easiest way to implement an HA Kubernetes cluster is to start with an existing single-master cluster. The +instructions at [https://get.k8s.io](https://get.k8s.io) +describe easy installation for single-master clusters on a variety of platforms. + +## Reliable nodes +On each master node, we are going to run a number of processes that implement the Kubernetes API. The first step in making these reliable is +to make sure that each automatically restarts when it fails. To achieve this, we need to install a process watcher. We choose to use +the ```kubelet``` that we run on each of the worker nodes. This is convenient, since we can use containers to distribute our binaries, we can +establish resource limits, and introspect the resource usage of each daemon. Of course, we also need something to monitor the kubelet +itself (insert who watches the watcher jokes here). For Debian systems, we choose monit, but there are a number of alternate +choices. For example, on systemd-based systems (e.g. RHEL, CentOS), you can run 'systemctl enable kubelet'. + +If you are extending from a standard Kubernetes installation, the ```kubelet``` binary should already be present on your system. You can run +```which kubelet``` to determine if the binary is in fact installed. If it is not installed, +you should install the [kubelet binary](https://storage.googleapis.com/kubernetes-release/release/v0.19.3/bin/linux/amd64/kubelet), the +[/etc/init.d/kubelet](high-availability/init-kubelet) and [/etc/default/kubelet](high-availability/default-kubelet) +scripts. + +If you are using monit, you should also install the monit daemon (```apt-get install monit```) and the [/etc/monit/conf.d/kubelet](high-availability/monit-kubelet) and +[/etc/monit/conf.d/docker](high-availability/monit-docker) configs. + +On systemd systems you ```systemctl enable kubelet``` and ```systemctl enable docker```. + + +## Establishing a redundant, reliable data storage layer +The central foundation of a highly available solution is a redundant, reliable storage layer. The number one rule of high-availability is +to protect the data. Whatever else happens, whatever catches on fire, if you have the data, you can rebuild. If you lose the data, you're +done. + +Clustered etcd already replicates your storage to all master instances in your cluster. This means that to lose data, all three nodes would need +to have their physical (or virtual) disks fail at the same time. The probability that this occurs is relatively low, so for many people +running a replicated etcd cluster is likely reliable enough. You can add additional reliability by increasing the +size of the cluster from three to five nodes. If that is still insufficient, you can add +[even more redundancy to your storage layer](#even-more-reliable-storage). + +### Clustering etcd +The full details of clustering etcd are beyond the scope of this document, lots of details are given on the +[etcd clustering page](https://github.com/coreos/etcd/blob/master/Documentation/clustering.md). This example walks through +a simple cluster set up, using etcd's built in discovery to build our cluster. + +First, hit the etcd discovery service to create a new token: + +```sh +curl https://discovery.etcd.io/new?size=3 +``` + +On each node, copy the [etcd.manifest](high-availability/etcd.manifest) file into ```/etc/kubernetes/manifests/etcd.manifest``` + +The kubelet on each node actively monitors the contents of that directory, and it will create an instance of the ```etcd``` +server from the definition of the pod specified in ```etcd.manifest```. + +Note that in ```etcd.manifest``` you should substitute the token URL you got above for ```${DISCOVERY_TOKEN}``` on all three machines, +and you should substitute a different name (e.g. ```node-1```) for ${NODE_NAME} and the correct IP address +for ```${NODE_IP}``` on each machine. + + +#### Validating your cluster +Once you copy this into all three nodes, you should have a clustered etcd set up. You can validate with +``` +etcdctl member list +``` + +and + +``` +etcdctl cluster-health +``` + +You can also validate that this is working with ```etcdctl set foo bar``` on one node, and ```etcd get foo``` +on a different node. + +### Even more reliable storage +Of course, if you are interested in increased data reliability, there are further options which makes the place where etcd +installs it's data even more reliable than regular disks (belts *and* suspenders, ftw!). + +If you use a cloud provider, then they usually provide this +for you, for example [Persistent Disk](https://cloud.google.com/compute/docs/disks/persistent-disks) on the Google Cloud Platform. These +are block-device persistent storage that can be mounted onto your virtual machine. Other cloud providers provide similar solutions. + +If you are running on physical machines, you can also use network attached redundant storage using an iSCSI or NFS interface. +Alternatively, you can run a clustered file system like Gluster or Ceph. Finally, you can also run a RAID array on each physical machine. + +Regardless of how you choose to implement it, if you chose to use one of these options, you should make sure that your storage is mounted +to each machine. If your storage is shared between the three masters in your cluster, you should create a different directory on the storage +for each node. Throughout these instructions, we assume that this storage is mounted to your machine in ```/var/etcd/data``` + + +## Replicated API Servers +Once you have replicated etcd set up correctly, we will also install the apiserver using the kubelet. + +### Installing configuration files +First you need to create the initial log file, so that Docker mounts a file instead of a directory: + +``` +touch /var/log/kube-apiserver.log +``` + +Next, you need to create a ```/srv/kubernetes/``` directory on each node. This directory includes: + * basic_auth.csv - basic auth user and password + * ca.crt - Certificate Authority cert + * known_tokens.csv - tokens that entities (e.g. the kubelet) can use to talk to the apiserver + * kubecfg.crt - Client certificate, public key + * kubecfg.key - Client certificate, private key + * server.cert - Server certificate, public key + * server.key - Server certificate, private key + +The easiest way to create this directory, may be to copy it from the master node of a working cluster, or you can manually generate these files yourself. + +### Starting the API Server +Once these files exist, copy the [kube-apiserver.manifest](high-availability/kube-apiserver.manifest) into ```/etc/kubernetes/manifests/``` on each master node. + +The kubelet monitors this directory, and will automatically create an instance of the ```kube-apiserver``` container using the pod definition specified +in the file. + +### Load balancing +At this point, you should have 3 apiservers all working correctly. If you set up a network load balancer, you should +be able to access your cluster via that load balancer, and see traffic balancing between the apiserver instances. Setting +up a load balancer will depend on the specifics of your platform, for example instructions for the Google Cloud +Platform can be found [here](https://cloud.google.com/compute/docs/load-balancing/) + +Note, if you are using authentication, you may need to regenerate your certificate to include the IP address of the balancer, +in addition to the IP addresses of the individual nodes. + +For pods that you deploy into the cluster, the ```kubernetes``` service/dns name should provide a load balanced endpoint for the master automatically. + +For external users of the API (e.g. the ```kubectl``` command line interface, continuous build pipelines, or other clients) you will want to configure +them to talk to the external load balancer's IP address. + +## Master elected components +So far we have set up state storage, and we have set up the API server, but we haven't run anything that actually modifies +cluster state, such as the controller manager and scheduler. To achieve this reliably, we only want to have one actor modifying state at a time, but we want replicated +instances of these actors, in case a machine dies. To achieve this, we are going to use a lease-lock in etcd to perform +master election. On each of the three apiserver nodes, we run a small utility application named ```podmaster```. It's job is to implement a master +election protocol using etcd "compare and swap". If the apiserver node wins the election, it starts the master component it is managing (e.g. the scheduler), if it +loses the election, it ensures that any master components running on the node (e.g. the scheduler) are stopped. + +In the future, we expect to more tightly integrate this lease-locking into the scheduler and controller-manager binaries directly, as described in the [high availability design proposal](proposals/high-availability.md) + +### Installing configuration files + +First, create empty log files on each node, so that Docker will mount the files not make new directories: +``` +touch /var/log/kube-scheduler.log +touch /var/log/kube-controller-manager.log +``` + +Next, set up the descriptions of the scheduler and controller manager pods on each node. +by copying [kube-scheduler.manifest](high-availability/kube-scheduler.manifest) and [kube-controller-manager.manifest](high-availability/kube-controller-manager.manifest) into the ```/srv/kubernetes/``` + directory. + +### Running the podmaster +Now that the configuration files are in place, copy the [podmaster.manifest](high-availability/podmaster.manifest) config file into ```/etc/kubernetes/manifests/``` + +As before, the kubelet on the node monitors this directory, and will start an instance of the podmaster using the pod specification provided in ```podmaster.manifest```. + +Now you will have one instance of the scheduler process running on a single master node, and likewise one +controller-manager process running on a single (possibly different) master node. If either of these processes fail, +the kubelet will restart them. If any of these nodes fail, the process will move to a different instance of a master +node. + +## Conclusion +At this point, you are done (yeah!) with the master components, but you still need to add worker nodes (boo!). + +If you have an existing cluster, this is as simple as reconfiguring your kubelets to talk to the load-balanced endpoint, and +restarting the kubelets on each node. + +If you are turning up a fresh cluster, you will need to install the kubelet and kube-proxy on each worker node, and +set the ```--apiserver``` flag to your replicated endpoint. + +[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/high-availability.md?pixel)]() diff --git a/docs/high-availability/etcd.manifest b/docs/high-availability/etcd.manifest new file mode 100644 index 0000000000000..3567a6f4a4fa2 --- /dev/null +++ b/docs/high-availability/etcd.manifest @@ -0,0 +1,104 @@ +{ +"apiVersion": "v1beta3", +"kind": "Pod", +"metadata": {"name":"etcd-server"}, +"spec":{ +"hostNetwork": true, +"containers":[ + { + "name": "etcd-container", + "image": "gcr.io/google_containers/etcd:2.0.9", + "command": [ + "/usr/local/bin/etcd", + "--name", "${NODE_NAME}", + "--initial-advertise-peer-urls", + "http://${NODE_IP}:2380", + "--listen-peer-urls", + "http://${NODE_IP}:2380", + "--advertise-client-urls", + "http://${NODE_IP}:4001", + "--listen-client-urls", + "http://127.0.0.1:4001", + "--data-dir", + "/var/etcd/data", + "--discovery", + "${DISCOVERY_TOKEN}" + ], + "ports":[ + { "name": "serverport", + "containerPort": 2380, + "hostPort": 2380},{ + "name": "clientport", + "containerPort": 4001, + "hostPort": 4001} + ], + "volumeMounts": [ + { "name": "varetcd", + "mountPath": "/var/etcd", + "readOnly": false}, + { "name": "etcssl", + "mountPath": "/etc/ssl", + "readOnly": true}, + { "name": "usrsharessl", + "mountPath": "/usr/share/ssl", + "readOnly": true}, + { "name": "varssl", + "mountPath": "/var/ssl", + "readOnly": true}, + { "name": "usrssl", + "mountPath": "/usr/ssl", + "readOnly": true}, + { "name": "usrlibssl", + "mountPath": "/usr/lib/ssl", + "readOnly": true}, + { "name": "usrlocalopenssl", + "mountPath": "/usr/local/openssl", + "readOnly": true}, + { "name": "etcopenssl", + "mountPath": "/etc/openssl", + "readOnly": true}, + { "name": "etcpkitls", + "mountPath": "/etc/pki/tls", + "readOnly": true} + ] + } +], +"volumes":[ + { "name": "varetcd", + "hostPath": { + "path": "/var/etcd/data"} + }, + { "name": "etcssl", + "hostPath": { + "path": "/etc/ssl"} + }, + { "name": "usrsharessl", + "hostPath": { + "path": "/usr/share/ssl"} + }, + { "name": "varssl", + "hostPath": { + "path": "/var/ssl"} + }, + { "name": "usrssl", + "hostPath": { + "path": "/usr/ssl"} + }, + { "name": "usrlibssl", + "hostPath": { + "path": "/usr/lib/ssl"} + }, + { "name": "usrlocalopenssl", + "hostPath": { + "path": "/usr/local/openssl"} + }, + { "name": "etcopenssl", + "hostPath": { + "path": "/etc/openssl"} + }, + { "name": "etcpkitls", + "hostPath": { + "path": "/etc/pki/tls"} + } +] +}} diff --git a/docs/high-availability/ha.png b/docs/high-availability/ha.png new file mode 100644 index 0000000000000000000000000000000000000000..a005de69d7fc860a25fc871968bbfa8fe32d63eb GIT binary patch literal 38814 zcmd?RcTiJZ7e0FE0s{I17P_J!B8osjiUd)?LO|&~DAGa)A+%6L#0J;^2~7opl+b$z z3sOQ6k^rHK5Fm62EtGqLzTfxFy}y||_wVauV&%Aqnb~5GGdL*musJ#dIenhr1k6& zubAsqi);F&Xy;|Lsfdoo;b24n!%H|d z$IAV8EUP(tBYj9`;eqE?Th~M%U*O_i!U#8f&Dy$2ykN+y@P|Gj9o=U7}&Ecc0QYZXgR8i zTR@%Zy&)z0YCXJ6+(QOw1vb=43`|Ar;k(N_R_28o(gU_kh8Ex^Gb3$QN9nJ{IKm4k zX`{eG2ZK)b2Pl)Zs#(1-q3SL4}(2dO-=8~mXy6W~%=!9^S=@;Jc*;q=yIhMcS% zdXw0LY-zo)j`Sq5fG=Y>rZLd7^Q8Jb`1?Qu{j+}&ybk^^1~7pi@CUOE{X;)i2f+{c zgHaYZ6bg2z0kG14@3*D@roZt2tu5GazinuOU}#%g8`g(Hfc34F$hgXz^%4jJ&3qgY z5<#SayfU7@UFb4YR#paxtKTE_l$GEfe0-H{i*RyESafx+p6B6t{^AAJtk~WxDk|!f z+TRuufF5^5M{e%9oa}7G0HcSUT?Qm%H;A%2-SgHrMLSxku+spZ`8KaOI=n#d&#>PE zYTpC|1ZGh=M|`Uo7Y#q>H+?p@WYw}o`+Yn-* z`oo9O=eEMmfXK091WtE_L6|FfF`pE5S z$S!%w)&`#WJb$NflQ)$A!I2BrF6(v>oC|C&yPRz{TXKz0Mrw-2c z7w03+=gc`LynK1ES{b{R)jS;fdGxdE6r?z*EOWx#!eWj*Nb^)WEOHw#{@mWtQAX_X z#g%z`dF36`X*-(1Q-FleyW$d+I@Q%t`LAZMwdc>CE$Z~_NK*dNCSzLAz+HxUR&L$1 zvNhxOC}^fvil%HOc-fr*uMMDnZpsYay&ZqPc4jdCoY{RUmCB{ms7NAG0&-Ko+#u@K zI}G!%{*_g~0mgzyZyrU9Bo2%eGTZRu3Fy&`fol<#EVe62TP8X3&kH6^j@WY6{5x)&pO<8-+4$}&DUH{~Oh-Jc4% z1B9iG`>S)f&d&0yh))cruvg%&JRN#?ejxf$8LiK#M|jS3u<@cXKwvw_(dd?qC0v|l zdU|b&=|F@DULa7$vZ8CZ>s~sLyND(8E!OhA4}@Y%%eLzx0prpS{>rFJ(`y#3$jm5?wFxBy` z^L`&1KnmeXK`+_(V~dryBO^Lc7SqjYW1#BzD1)_L|9~Qs&9_hH`jDg11QU}J?FY9M z?lPFBNzp#7lK6$I18Ga345o`nAB#R+BPkGCnna*R7ojHG6Yg@UXHZcohqG;;9SOzx zzvoe|!R6GFwf8sWKts=wP}qBYd2?95EoS@Tm0RH&l~Sj)I2(omY?fy`p9Y2}i``35 z(WT-(T7<=d>Ht|Gp+8J;2iZ5ApKE>$fnEBsSZ zZ5O(!@x_XigG#(xGeqmEqy)@t3^pqcR3`kpr<$M?O|SZIF(pnIdgo}^KhiG&avL)5 zKI4B6IrDZpeg?}9VN!B>e9!eNEV(qL%(baJA-<2n&5+5 zWBQ#rHw)O`BLs35vp%6MSdPn#bC*pPYQ{0XLogey9L>?gA{6*tN|PSNA&%7IK0#bx zMH`&3Y?HdA&FLxNWKQZ@N&s?gyDuhaxS~%t3kuyLW4sl}Io?R(uc}VGu*Mz^1O^sdx%YGQkUNM?^60>gf@D?y{Re>*+rtaWOHrXnG9ZC>ht8HS zw=wRu{=qbpyACSDJN>G)HL7f~S=>oeGe4xM(Dpw!?MdsZ^*hOARevP6jsB21|3SFP zFZkC#Hzmp|PTS(@y1vP$TK^VS3B9?Lvrc1m|9l{6GI}5QtY5g`J72Rm&HbXW0MJp0 z+6`(OwlN7dvpA2m02S4#2pYFT_%w+mjV(qr4TXi^WXkM4<3Sju@iW2(&zz&IfO^F6G8Ut@HDLRZGiRNZ zhc_4mNw0c)B~6)ZogO;EUZ?v%%Hn4|bbGJiGzj5BeO9q6gvqMtup5h9xx>5_VbxOL zkn|sEMV=47SOZ&O&hI}ui@UH6Z?F>?K8SN0$jY2xK@$|E9|Uqb&U!y#Dv@ zACaPe%Nm&{0ZHF7=+Dr5{?nHJO}~4ff&NXu{^u@zP|P;;?*_wZZ$=MRa2(*9LFs(e zaW0eImobQ;j4Ul(LXxFsW@d7;v(Nr>0lZCrh0b-r_&^&DS1KSVm>+pGR%ALH!oOvjLZ{BP%%9k-K`Wg&_6k97RD;wL}V<57!ua+51 zPZ-h&%GXs>pFCluQB6ug`X;F<@SGzDMK05ZQV@mBAK@Dd)!8){L(2gvR)&@ra3OhGlzx&Q3ZD_@33`?%?mbEdLt|9JTLK>l2o_@OBYq>kwW8eYt!T!M;y zr48t##^Rd1p>$|rbN{yrLq!-UYb}REc|_G+*haUu0;I*o!M^CqgFe4bU~phSMNPDz z?w|cO^qqD(9(o-W>VQMrb+YRK`*;a_Jlz$rE_X||qE!b_5r4!>=aq93uq^3!J)r6%P$kb4MY-vhb~Cbbx;*j4kAJK3WcB04cq^4!6hkQa5j5NblSPl`kmg}S`F z%s!M~mKD|}fewxv@P)%ucPh%t2=k&MQIk=5|3_IFHPhGk!aLuNJ68xdz~!3g>PE*V zBsg&$JeVE?iYva(N4L8p^W(?H;LFBOb;^KT4b_m{?XhN#au6(jiQ#`4BcyzPf=ni* zJ)sOrj9>b5+5oXEMh_%%eQ~?djV>jm4|nvj^ds!e#sKJ~M}q4l3$ByCDsbD^x9l=f zHrz3cE+dw%)x4qQ-co=Vm1%JjmpEr`gA_IW&*>GLV=S2DA)sn8m?!_;v=KmvBVrb? zG&FnwCH(04v)=OkOuC~+P9BUN;S}Xqn56`8qZT;FdF0$N5%!9zD)fGH8?8VBd#!kE zE&VnQsJZurm1dpie-Cm}VPB#ebjnWZZU{epckVndkZupC2W=Ffp!xav<#KXze{_R= z$;%tlA}4_QAEy~Zd$}60SnQV-28#6G_D8YDS6{989FAuL^p$`YTRqp{z%{m9{0iGq}>rv zrRzfhz295=I*7X_?MjUQ{0JCfFt%-;EbU)B;!Gi#^Oyo`y)Z}E34(~`SWG<9eh4MKE_5y z{LkXSGxYx2ynUC0xZRqgah)4Eb7k3gLy3QOdG9`GjTg#x8q3_FBbzNf`N)tp3emvO%uhrxJkqJMO-UJU6-}Mn2!I8WmzNtFV1bOHBQ>fUhSrrK z-~1R02{PFnxi$59#ar`CbM7N`(B8xBgI6P}nXU?Qm%sxG>Uz%ZUg;a~o*gurQ0+vT zuS19Kp{U)j_?4BMC*~VQt@O%}LA1(f15&-Hdc}Cg;+(-@0*Em(%{N4dHeO1*CzL$yI^>s^W1)9e;+45`gKS0M3vr8-M)l_!zgN2qGN zuuD?OkeQYLfpbW=-Oh5YI^BljNzk%!HKOVRwl$Mr@8N)7rRPtjyj0;^X$#QwY?t4NrS@Okj@| zRQqyd9Y-O!V zum5`38q`I-cWH%2QCSPds`wR`QTk)iS9q@7BK&AndhbuEyYr2!nvq;y+tt4ghUDf7 zRQmZ$`K{}BEVWbDY7y>>ha-a53;FpX9Y$z|&3UEx#?gV9uF;QA+;XxqMrK4S6tbEz zbJBcJWKnY!Icx29&KmO;INGKB>omr_qxFq|{zU+ZMDlcs&X(;ieVCYU)%=)#%gkED zagv)EXxW(j{Cg!S|1zO&oC2A1A1+er%aAC4J*sNBG+ylM`dRZs6Tu-l+|${DUkCry z6q4c&ffq#A`BW10OFtrZs{`B%)>A+%KA%rHx9PsVyA0L#4RK7jwJ;IhvcG5d!C_sR z%v!oP{Ru_fu5os>yZ%A%ifQR^_FSzKa`98%+q}+)KbNK*_$#fi!chvn0c75f)l<-e zVzG<`4;&mw-fZ$uk4#+!kEAT3BS$^YS%h80qOfgby|h{LD4+hw#upanqN|vuLdMdq zsMI6NazRCkwnv7%-$WO^yp z$E@dgt1Sxn$Rkp>=|Z;oO{STRx$Qx=zcVq98nQPeD!G?AhYg`@{^+>x5B#UeoNl>` zqAwp053{9APwj%h+SsA9t7O;vP6cX`>PCrf*1YO0%JrmocFQ^D$!;RzIdyyV&hoan zL7&kcX+dOOr)pF%O1;a}M0f^n#lLYl+@1mK>p%k^0tv|dN8)?qIJEzYz63NtUJd<1k9`U9X9S>e{EMa2MP>7d;X5OuibSqKili0 zB5!HSs`KWkAI=>urWBdM3;Y8MQ z{hnWj)VJks+sv#e@CyGB-ryST6Z^TIc^PF_@ z-q!VqUp2ywE-da*>ZV?&wx_Sbcoq0h7?7W~Ed8uctuMImFdB0(ngtwyGT`1<(3rbsu{soWK$uc9&T=VKC^un z_mP-vg2IIbP<12Rhk1p93JW7Ao8EUTm{)3%jj@YWGo!ER*kSBEXf_R;P;>-+anSJi zRwN|mu?k+tH2elbg^r9^@52B)yHUFFVymUBN`dUL%Goty7Dm0Nu%qX(cdfOxwfaXIUJ>Haf32repEOUQ6pIqUQ2x%B zwCv%p*s!o?{yehD|DPPL7=b+R#Fyisb8thN z7*DI;#W!oBW+(^5Jkb^bcGnBuG86%f9&))OLqnIuQJE;tZRz;o%r>0f(Fmeyw)T$AK_F+6$n_4rZ z`VdqzwwT+D;9?d8?*np=67)Shqx9_A)c~fC<@TnfnF3+vr7eh)sIArMoHd`x_Tzyl znH=*Qu2*p$wk)FtnY#ULk(wW+Q;22!Gri|Fzdqx=do^b*!pYzpxo@0@l9aBR>fX8KMoI(gC+2048VIVEok_{>ipN} zhm@p2GMN1cJ*F_={X2@5iJoU^=$lgsP)vePjx<}bhyi-*TXfrU^-VR;?Dr_aIa$Z1 z)JN|>dSs9XPu%)=xAAtTv`!+XltRp}|72E}kYfsYr=CeLB7woqQayv7+2JSAu(p|v z-h(7&ALN~0Z*#rU#JWqcAQ+%Xp27A<^M@J*B09Y3DFL`%ikiRCl!;8PGCY=r^N1(X6reFgV6AXF)%4TUfRIR6K}K|^ns6!|Nl+mAJ2 zv0>4eGYNN07`TfB6vs?(`9r1I5<$|7PNE#&v)L6pE13$|vDw!}q%_c7XH@i1`Q}4l zu7)*E?uvD@J0gd_!bcd)h(tPn^T|^=nw%&?32HC(WYgoaw(Bq}ZJ1QH3Zd$WJku;M z%XhX3jsw#eu}%4PG5DG!@Fa^@)Tu2_v!%jM)dIYu)guer3i%2iN0CQD0kvdGj(?%1 z!QH#BcT7L_3O|M1DpD1bdL?6CGLeSlpj`u_|fSR?ot_j0_C40wk zRF0wRu>t;5_J&$%6-K#OHMfc>!CD^jMFI#+K%KdCHYl5QK#-XqfZZ`U<8##eAZ8MO zG(*b!B!g*hj9CoimRAYjqAun^d|oZ}G;z_jDMvX($z9YYxjdMA*jpYl`0U^zjPrQP z&l{u{N+@1%O!rO8(p>xLwsn785lR68^neklJCtA{{3`fWvmFDj%VcjfOX7za?uQ&+ z-I~~Pd!^T4MgkE417b>bO70Vm>j8>aehQT<0nGx;=gdDS5b=TMhXEI@agnUA1F6Ta z>0Boncqqk`rl2h-G*fRyFb*q5NDKp0*0j|5jrV_}5Eq|seK>0>)#a}pp}4}Loo3$~03+$00&>g>3Ek>7E6dsNg3hedYlJuG z`{+|KO5lwOgctu;TrdnUhQlut#7w(Cvlk^qnzT9gJBr^AbPh#zyfY~5kTHL%2pOMe z$Zs#vvOgrl>oLTXa2OohYb7StNB!k^#p)gX7 z8)HF&xLG5V;G?kPri~uy6AXnt6_)00hsk?Dpvd(XBGcPoy!}wbkhq-p_VsmVm{gp$PGK`OrmXc!Q#hW_U&;B!V#CW< zDr`_Z>*j_vzvtScihtFVz16Dw064P#5H^3Mpxxh(ET-7${MgyxZSJ_yTQ4NGvQlw= z)CuvT7Uj#oZL}id9DVO9tam8P1Xlrr+JoNU%h8XbC3{8_(hkJ{z{oG|A(Z}( z7|{1PTsQanPDrqS!&VnmBY@%iSo{Lj?0O7yH6&jVDHIAMouec!V(zBa^4-8~mq-j& zCWN64RddBMgEP&o?+pxXD|G|sW=PqSH5@(|V*}Dp+X!ti2#hfeSKqWuGu3+l5Q2TU zhry(Qe2-wl_?@|m^-EuMhSKf3vt@|B4_yrh8x0tUE>|H-1DHqoYYW1MRRv_6F?s*2 z6oaNp<6VQrvi?SWNKW0#5J`mHy244Xp$d#*=WOA<3DWh-?zKyioP=u8b&K8NaKhVs zAB#wVs++P6$7+cI-BpDrWlIox9@0ora3VSs>AtfH2)^Hry+UUB%^dg5I~6EKeFn0t zYZgYwULMggYRe(lrk&22H0!JmPb$kQaL~##<|+;xw(eHx#)v%E3JXau$lYxV7_yMh z8L(n8U-!J0$kECigtvB9jBH}r9ZYlkmvKUaU~0cXR12uv($ zR@rkD4FDr{>HK&#S6Z+i4Fq;jsX^Wf!>ylYluY+^bL^ZrasDi`=&lcs6zehD4Xauu z$DOJ2+M~wC%#Vn%TkHCqU=M z_gZ+!sh?jRb@&ONW zTSnhPfW+}=V+C?5QiYAt17WZ-N!DHKgQZX*uj^~w@krm_@oc_cL=bcA2kdMPDpYjL zWRE)N)evG8mA)DBG|Z11_ke>vijR@*5cGiCt(&}^%kGJA8|Kr&)dP@2)oAJig) zwP0|5KiqaKBVS!0dprK<7bko_@*IPy_lDD^AMcMGGaqm7l|{>Nahc4y1p7W)t_H1p z_df2R*MyznXm^hihQ1`^N!&MFkvOE8K<&Bsy?Sl(J>>G&8|`bqb}a4R#E+;z4J%X^ z<>#&cGx0}TJHvOz7EDCHCJ2N=;y+AIt|-HzjC!)EwPCl%^iLS(C)yc_Q(WWJWMyqS zRi~_VS0z9~qT(9OQD|_2-D6?F}jgY+|v4o`1Ox@(?f}!{k)8++r_qi{q;hFGU zVo9!YaBP3E0~`|hvBScf)BeE&OHWjdHv~ab(Oai>7gm0$?sqpbimknX%C9`^nV|LH zLhixUb283t0^d@r!C8c#`7@J?bTT zm&xi2pFW)oG%HT(u()U022oVxhHDE)ru2+jtJquV2^Z((R}S0PbM$ab1hk(am<%la z?B}G%F(noq>dxp}zkGLvj9i4w2mRfM(BsopKg*tr*7>W?xjlczYQv&{mkmhdP6RM@ zPO267-d@YXBx~e_Oo=8ABuBPHd%~y#!h%=Xjm2P2?>gzH;?WD%LEk*js?GSY zz?4{SyIyTn1c?iq(}m^z#d(oUpVw8UauN~t)Eaf>LoAk;jdA6Pd6AmAh>juRZA$fY z6&h{79$ZTlg!@e>k+Be?FtY8olk>Z&*w@Q>3o{IO^UDA3!X$KF*foX{wW@Ql0im8t zr^$riooC{+iC`ksAmpb}do;n&az}Zu^`+lpkZ1Gdur+vZBzV zs{lxvkLQ_t%;^yc7D0KFMCQ@2jEmUnU5}Gq$;;vsrs=EEhXRI93rTfqwVseXw-Ehq zj*r96WkQ+)KM62_m^n)`e2Q=O;6rCbEO^G!@Py}E-DqHT-aTgm^24x1V6=s!0V83Q zpY^Ob3u3frNmfPd~cHv1+_K{1kH85S|7eC#Nba)*h{ro-JcoK8J zATvyCE&7$(;eASl<&LK_oos^8g*vdj#5`A+Zq#^jEE`azh+fjd|)?evms&o%uG(zjLg1x`-H-th0w z@(gZ8`k$If?iJGi%m6BJa6NiXmS1{rcIU7kt>IZ-N&;W#bx2M2>(!3y&a7|bt`O7| zA@7DK+HGL=ht5G{duR@>>e`r`A=<6YujOPrMuogIz0-K(_}_>k zAAMtbT|`jMAG~2UTCEn#`^s>NiJq=a{{>yNYOIt7BVENiY-4#39l2ebJ~75?qRZb` z2J%<&nL@S-o3M|Po8tXDnB^cB2y49u%_%wk1^tno^0$iEF*AuE0nBXOvNr9qTh#kp zAdAeOi@TU0a4*6y;=XWwg7hV85USdz%0mmw(mv#y-z&~Lo_-dDMFO%qolXK$wRtPX zZxL3vi|Y`E#9Nrt%Mu?UF=0{ zNj9WW=ApPt&G9#;z014@`sZJCsEhcnWsTOfvUe!!dUX8O1zB+WR?P#?Y*7qCi!l0eF7D?y=P; zeIl9mJ}Cyp*$WUc^UKmJc$A8CvsA*{WVaLso|=4L`tmh^{V>J# zlNZ)#0DA>Fx$2nJvUW{0EiW}%av1W{t7_Q1gLKrt>db}+Vyet|folN%77O_rh&_r; z^krkK+kMa+a%-{EKZSQuXCu({u^s>Cqg&@YMROOoi+EMEXVz(gnQKO+^?KJ@dpguh ztWV-V2iJ^N^MyTiC3*bW=`ZY={knIoCYXtlNpnJ8W&NX%X>rUr-w zpxIWkNmKz^OY-(c6!S_LEtLxneF7!BNS0uaeW^mIB#l4rsnu=Z`o>715^vmOK;z%01uxqGp;q32wXuZv?!b^V6sAMae%t#I{d za{EL+l>emWn{qQT2{nsct87tz;+#C5uwbc2$I;+2Hu#+*jlqvqwj}Kaz-WmJ!D@%@ zMD?ZEl5;`CSmZXqTTr(WlsV+CkIOK+onfEe%O`I2nNx$B6CW~?Ies`puz9BizDn6Jj!9~s|QMJhjcyEe>1CqWT#ZpIg>u2+Zu`}6c6l*hRF0)wmJ8Y z#S5_=o@!GnnGVV0Y7;`<>zBg4%ZrmG9I7u=Fr>&Q~XH}9IgL%~rXxf`6fUbN&c+;|1J<3*}SO2o{C06}@%EG!?`9f;dq1@Nm zqU!(Dm6WNh7HT=^Em_=r!dqgjT$+);p(WCF_BXQ&0UJT_1#u*5raL1UILwT9s zDvLG0nSR_OB$=D%iT@O@{BiBNl80D%E+tdrh`bNxildPja;ra#YWMVwZm~?EbQD&%^IZ%O%&NbT8F5v)@TOZl(N% z{gEx#4^+oehJ{A7-S>{^KL<(w^7j1pL+fC(58jyeJcG7JVt+?@WhrAJYTYCAvMj;z>H^P*+Fr|6GYM*L zEpmWP;!i9We~+_r_=z}$Q-H7lbZ=c={*@mv0 zfBW{$*wqzFXPiS66ih+Rit+8+FZj75|18^2BA`9|PITLDU3JhrtS@-eI@M_FXaC`N z_7uU{XXl8P+dGS@($eABO}kwUYqQZ;h`^p8kDLc_6TS17r(Qr|2B`}#oX~t4wLJ<^ zN@5PG%|$I)wZZ*a6FTW|*Ye`owvMXABv+@z;?0XOj;-W}DWC%bhV<=ZtZVwriyk~H zbsJJ~2IAb{SwF>)beZL;hIE`M*giFBc+ z8|6jgY2tFuxtiGVJ6{m0woz)zz=-;z4^mIhpXUm1Ot0lDQ~-rm@xBq7yBdwcuuT+Jcz<;nZBH66)X zvk~)=P1bT_o5uq~#a9VMav_~X`XR{Qsf!6W{9&clTQoB;qL{Gt(_8803$*6C0*tF} zqenqXO;Qflb4|!1nzu>BBYJ^9Q>&U3-&b-yGs%3BgDe!U+?>=B6U-+nAbCiPG%zqL z(Txy6ZEtwye8`u_ zTQ0+TPQbUpZMdba3hVL1oe-1Mqh~*F@2A_&?_CYSf?*%%*c!*R%;05z)or#4i-vDH zJ?VAnOW@)hpz8K0#D-f)fj&cEEN{DyP`*lY(vLH3t{qF#eL3GPV2yZf^YBQ!_JY0m zeX5<9Fd|G`DXvTjTCwQuR@e}$aC8yO@Pks~S~)0lC^7+alSwAQ2|W_tIva191}{CX6!_Ld&# z^e7}SD8J4M*W&m=_s>Copsc?F+SS^LlmjMyEP@|qulu-FzRwC?U-}kJSFU!dj6^So z20kynp7hDD;8Om)fgg54zj2)rXW6kFnXn%wB_A3Yy&cpsbi`rvYR8sy@|@k_zvl1p zosi}qIE>S`yoS`AnAE`HVuvZla(FwRr0%*_Q$F`_afw-j>V0VW{q!{N+N*@} z)CCvZ1)iV+m&lM4NFW`6gcH~xwh3V04|_jFztOBlYv$Z6L2KnaD?#5%)sYpWB;oli zU`2QZ3s?#9B}DA6RzC@nWv`arBgU2){f=eXEvOkB2_)^_aqhqE0Nox-ygWz8r0T0Y zrk7FizlOhVv7I*)Wns%Djw}?X_+i_&Cgp_*&yazY)tMJ}thZ;AfA1WpAeX;=Pw`fo zyc+~41!TjZAsqoF)d%?R+t*sjC&y#m;d$&&Fro)BhVFnQw zb`@YJM({lfc9>CR$`=6tCTFt({kUHCdFUWH$nOJXaU-P0H<4a;lT%pOjc|7Uvp=#7 zm8Wf;%q-%{9jh{8V;dJa>*tUE@>Tc#{G@(+sy|&h1qq!eEqVJh?GW&)e@J zE{21LXMQQl6-=J)j-D~rP@f0_WbbQq{PEe=^P#(6(auh0 z2!3Z*@?+S_+uF|I-P;5@k$YyZ)M0eDD!Q1SaR8}3!?j?|AE|ws^JUvP$wSe0XbHTm zOo-N}JQ2JzXwsb)-JW1PZ6SIK!7cuLMoQ%5|l5<+5! z;5EH_ncH3=MAsPRJyW_3>`b2B{bCVP)rBDd0ujo0&5bFa?M+JCAW$fA1?!+na_<+M z1&#DE(^AQewdYo09FXb@XSh2j^SRy|Ce-C*ItgU47bak`n&P*66uup7SUU6dZ2Q?> zkd9}zH?~*jZOCBO=4gg3_Fu85>_=PZblYi-QZJTjMs4@p0}t`LpQHy99$7u!=L3Nu z>K%Sx8J7{6K&FL>SeCNDkYPlCODCi@Z7$`8*l>tdR{e?P&bf!bY1}(+tM8e7z!u0I z^14MsYd>N@;ccUObL|vH2clxbc*N`F-Or62_p>)MBZbpJQrm4PyaCSlN}&c+jP9;A zPG|*0X#z$n%iKlhAy=-FnDp;#MbpxzY~eL)amdF%#1*F_kK^N6M6Bw1M$$G-_jbZL zcfzIKML-6YlJzV_#sU;d6Q(Cu7H#f~DY!@OHJAqTbrvSjhVJH04et{UXNxu z*jtPmI&PO{FvM)O^B4~|-Fbpn3s|a^+-G)bsOY~n!gHU$u(C%2Ng;V}#K)lZJLKKn z2ks|_KoWwme{nNNo|Ti>!Dgl2t*T5mk?uY|rc5vX`E@&HKta5w?a7jUje z!HIkHW-8{HKi$3pI#m?kU=??>guRd3a(nsWjoIt-Odoktq5Uf@U~W|?sbkD$uWctXdTJM&|@AW>3wZcq7ohv(vMrfSDI zX=+INByM|>@x9p0m;AuzfoMztU8V5QB>d14`V7dqM-=#`AE_|e|LIzGti=S7kc= zRL}tpSO7sO+6lc6!vKtto2RTnX%unpfZB&UNhXu-y&AU!#HKMrjh%H{rc-(rAmy}{ z(F@mEZEgq>VTRUv$~z+sQ&jth{6BwG&HdC^-WroKx4Y(@Jhv`9dthYkMge&%IqXW* zo!ZIQUE1)#+#Mo3-zlWTL(j2vD?NJhlBnPL?Rr;K-iisx5A{8y8!2!v%Fk+dx!;#c z1e2Qh-W5T18#Enh!#qH-)ZO|g3G-}U?}Jm9#)F7ZL7SB>v-Fl+>-Bw$> zcXq9%@j#g|q}^PPB5kAh+Y<}zy0}2?<_eWb5Vf#D74X=48bg#puVFehr?gGZie7t&4Nf|tybPBoc2m7WG|Bow;A$hKQ z8l_Ev(aFV$rt7PxO^z9hIz#rIpMo@pnE%*(fpQPTJ>`LOHo3dyw(QLUm0MueD{vJIwh;~x@cAjlbxRMMGh(Zm!j$$l@2&X20u^UP|LGy_Pb5U0XDy!(#&K>AAz4^<1!3b|FS`3}USy_5^1I}Iz$QC&= z$O&;bZcLz7VkbeddDw4v(uqLmOYMJqN>4Ktdg8{`vFgePLDKc6vRCalW;yor8k~ zeCvhT-K(BjT2FQYKvG6+4Z+#f6)Y?O1IZeEW_e2bLzdjY`&~c?=W{xB08*E|IaKA7 zW5t^*a1Z`+;6DCTj+PmuE&JXsbYc6o)%c&%2idn+DtH(=Pcwx6=4>b=EX>cB(GC!M zd;3sN@JLhCQ(G2W>Sbfm2h{6%AWa^PM(5Vmsqg2XLXVHJbS9>iTK)svNCt#nXuVYj z*;ALRO0*JoQ+H>eq%>WSFihvt+(%>N9Xbx}S74+kD|RM+V4^qL0fd}UEEX)5ELJSm zEO0nukHSuTex1e}Mv!Om>Ss~1k}}!S-LJ#L3=l&xc_<(lv6wEzKf=yvOnnaYK%5b)%{FfVGh0z(H`O}qR-4rz^o=;SG{Sxa<4*CM!3dFP5=qbaY-gSASKLIe= zq`pte|9Sn%!g7iBR;EYl3e}42d>kb9|IJ_m(z=VH;R6+`{U9qym z1ePfLN$7&LPF4K6KU_UTs+krA$h7!Mo&RZri9=Se^gSMS5I^0vss7cD@jpFftBzL} zrMl-Wvcnloe3~; zLHT53kA5JO9z~?@9ztaC`T_|o%!SUXnKarl{dd&B z_t&N!XrLvNMSm|d{M+Dq8n1o6@O8j?LTyhjo(CK{cn5gJAt!DJ?gaS!Q{V3^f0HKp?;ec*=ihGH30waNwkR6|DHD$!Y8B(# z*g?J|omY9oxKm|}nsSlV#vkYJ$`a4}Z;RZ4pl4CP)bxSj`I@r-d0vY%d2|5XP$bSW z>YPjzs7(LQh!xO>le9S+66!8!J?j`PV*I-xZ*V!f9#(G~iclRzg^X@*R|3w*^Z(n6 zj+SbG_lsf|I+L=!I^l-dai)r09BV`Z4&$u53wLn*V2($LD_FmKYb+ z$W+<*Bma4kKypLC95ai}x*TU^&A(%s{uGuc^8DRqU0a$W&!yAej7*E19dby|%72Ua zdcG<68*dh`P;qy2Q*Quu&G8#0xBji;(+L24kHX*&5Ko!~LyME`(3OLL>bPJ?9Iwwu&dba#K+?Ibr$Eh^o+ki+L z-|hbl(;VR5(o(u(1eVaFwF=LHCt}~$zdaJlJGZV%0BVS4YbVaD{*@sc6{f|ti@8uV zP}6F*X7u0d;NO9`fO@u0^9dxFg}5@db@;?_dH4U@be1p*%c}Gwm2DK+4*?~v+CX*}e~ zNr{qe=uQu`zikgUvdu+I)jV!nNt>@u^e+cRT?5d1(*<<^3VczCCT$8}b zaVYfSCPM|x{dcp$KCoxDvC&fTUw$wM%TH-bvUSgd*Xs*?{@#4J-AH%m11x!L2CNnp1|;=En?Tjk=YTOcK9Pmnf~vs zF^@j5)<(!+#i3iM!Cad}-!Qu5fnA7YCD^yuF>8mc=|?S@5}5v4p#7!87=m0EkCBmX zg%Cvo$ow{g3E={}aT$-9AA@GxF(MT)EQtIT0!dfk4)(a!m3kH5vEWJB+eFRe?m= zxEow|tgMC>i_G1NK1*R9ksipC^LAqjH9utu3rby+FaWG;{0fIo5rS+!DqBOkUMH$g zb#=-qDG}AVQ8B%~%fDMLD%ld)B+QkhrC)He|6Pvv$_##EO%dYfs~x*PNwGI@*c&X_ z52EG=98{QXA~v_Sw#In=DG^0-<*zxh!sI}H{`5UHNJX(j=l{jsd&V`@HE*M#D4?P@ zq97umqM}k2>CFlj5E1DmNbfBmoq(M#qI8Igf^_Mf1O-t7iAV{dh8lW@Kthst1@wNN z|9Q{vw2$W-Av=4oGHc2;*UT=5^8&7G)vG^S7r6?$T%?j69nF!Xx&=JF|J`35ot~JvT^v|bmFKA3CejD7eZIWxNl~>( z#gc`*$i7xF+gCNC%`q^BXug(&RGedv%mRx4 zpOAb}3M67`?@h>VTb?!gn%3{hF>q#Y&Kg{%|aPDIhfB~Vc<)zFW`p1z4&+e z_c6PdN@d-h{S0I2R&MRRmKT;gQBcA*k zl4lYB_N|N!li>}D<_k!k6ch?o+q&|2eY9Y>OSLSLgT_i&g)NWWq7akQ(%SxAk$*xm zo?X~=H%LaNaELNUnsvE%_D$z`pMH-QYvYn|KVL830*+^|Q^)6P7F;I=_HXZL$Dt58 z=N=2W1euO=7hdwbs@8Oh+7TVcSAP^r0Lz!;?-N9#2_GNdI^iCx#c^-IQg5Qw43@j_ z|Mw(#{v)F|%`{>J1&Z91{z|B{D&)YveLX>;G7J&EY4-k}z3)3|su>Uv;c*uCl*g>k zUOS#fwhE0&9=XLA(#2-jdgwfOR2id*4$VZ>d-@RLQH+ZEM{E|-8+s-tDJt8ya&_RoR3Og?}1>=%0d%*@QTKmhyeE`O~2+Lz=z2SY+v z?;9EUHJx2xm_A7BBt+S0h9xl=sJ=%K3i7z6QWm&^5Fo3bq=Q!V@^aWvS$&xf6PAjzO5zk5flN ztadd#06lf=tdlQ;iWy^|UF$8AzJ9&R$f$K^>t;Q7TIE3foC7ud!ofFL82%}sa=!sY zsn(pTJRil=*WUkpD(kl0MhbxdRif@~mchX^y`Vd7o@bmzQ}fO1dL3O{%AiBVHWAch zvhuXyYMr@{uA#{ni-tuqDBP&H`u^AAjN^Rn+I~>I54syzH=eU@t#ZoYYpckaOR@^% z4Jku_tQtk$yLOLAyXv7|{=N0_I!4uA*?K^Y$v|elu?dvmGvoEdU6p&I!wcTx3&N8z z7|{2LW`3e20+M>PlVqSKLcKfb#tw8gkdZ}smM>RdU(Fq~4q)rkf!=?1&Bqt^LM1}`_E&G~&H921QrZnJxbdcuVZId56!WH-Lvdu5# z^3;-&dViNe)ZOU0WMKT{`LS-pS-RuD*t7yFjIdhjJ zB}havTgpl#D0aD)cYvc)sJD&^Oo`HBZX#C(qw`l`uGMOHPc_Sz@{BLJutzbC`Gnkm za$&cg20+BxJ-+8q&p@(xs6kV9pAKj+TvmpZUxw)aqszXs|M{mcSr&H5Qj~*}EVFaO z9E&WeESDOmXNP$XOAKId>_z1?bgp;$bZPa;gyICI4^Cywa-#MW~ji0wTW! zDLPLqXS3TWlgCh|u{T}rWk6w+BYe%UZ~b=}Zt2w3GGvYsuNxc`lQ~MOi7v3++&<92 zViv72@yOJzRV5oIAGc9|7sV5D*iz3a@67Ma&3ryF^gt`l+2jgv5zC@0ur7HJXY$Il zjN4MxZuX@!&iS)o|&Pep?COO)+D35lEKciuC$^3AR6{FA|ik@amT(PHF3@Rd6}z_o*J07lC=p1?ihkk)tum#>nyH)QUUeirM{ zop0OO{;Hp?;S3uHhcknuIbevN!Y5s&)*~f)jj&c>ei(|B0&ga#gh`kTQlz zNe_XD+4dgpdR)!svAo-Y+v0G%%;mRVQ@>_7-Wxcft(Du>c|8+e^!P(I-kZ&;%aoiN z{Ns#f4x*@AhDGJvp`6J86+3m~^v@9I$Cc(Jzhvc|4+cTu*Zc-y*G&; z##e{0meSg7-$3&Vojy4ge94D(%Yr)KNo$>v;uvjqK;gmd)}<7h@pKyKR?c zA4bcv$4@Odg(qvLT%JrX{~kF&WCj#acL$=JcCy(oall>QQuN1ZaQ zWiot^d+Oz*^KvbEBhSLM{2>Ns%1O4M`#D#B*3Cx<)6X!+SNL|1A5?w zn)XV~p+ZqcfXPtgXIIsYuiVRMtv)w4<+s4^va7)!EBf)=&2zOmyE|Alb^Dw?pTVt~ zbQQO&hw?mA5YI6FWiRv4U#l&rZulN0`cYtpf{$F(<9wUe{`x_jrN9fhcaD6A&D31( z^hL_PiZ9O?FEv{zi|`SgI8runy;}y?vQ_(POEZ_%&4Brj*@XNKcW@bYkzqum3~1)E zu})hdvwL24{vQ>=a&xca{5*_|&;41GT%9YaK&@l|CXAmu+z<|PvVQc;Ld$y`w);+m ztjdv{iCI9-HFYirkBdU99N)O>UUEPB@Bi<#rIwTcOcknoFll}7jv@ykTV^(+EK>ty z!zRN2coX}MpSH(1?;4PVZ=wxYhXBBOXWgK!kMrMwf9fAV@C{yQV} zJQ`|+h$W8v`=2|dG!$t5XnN%DkI5IPp<3t8pC9?~?=^8#2l9$-&^!|CJF>CwxJng; z#Xw)*^UB}RPoY=Y0CGzF>vLSbxG{&2G*oj@)J}R@T(;$qyM^*7XgYP6IuAlGhF5a= z>o95X$%&Rz@d1u~mHXoi*&BAw!|sD0j4wTVsS)fSfAq%f+lQ%`X&-|j2L81{@2jC7 z7oY!zS?NKa8M|AC?s^?@KC5%ewJMc%&rHYQfxh=AXAjx?i-KWazMVOBM&k7=!vq3n zN}Sy6^ZZww@$QG~D$3^A!(Bfc)i2(+IAUQt z36iIRYs$*fE3K{4(pcpce?9LozhLaQjt@gvQs2PwkoV&d9! z1a2dfSO^!&3l6c{^f|C5TNhXgTKDAQ?0A{>L70z#JBWld7heiU%{2&m#D#(!2Qheu z4U=*_mnFiCRSLpx@efKfqOZaXVo6#ASm^eR|HJ)xq7tuXaf>Cl*}5 zH;C?hyaJJ)88LteGv@UKV%w57Q>8fJ8$=Q!m7o6np9d8Qqvitp2Sslzi6Cn3(>6Dz znErjLmpZH5N+*sIp&h)`^0B!LE5UpO!YbAJ5(1EO-y(;O%n{&y(he$i#<89@Y zsTm?~eDcuv0#Szm59~Rg5GriQz~GxDX?}oq(3ZgNJ`h1Hj8?D(W8J<}GDJ0C-PvRE z*ZUC=?tx2uA#&!b{Xvfe>n~2QyUO}ARu0VVVGFZ-yZ`EByXVwg-TEZb4wK5awE~zOL3~fB9ScLge=!?4IciL~a}#);mpfJmuFE zet6tUPq<4DLL}XcB$nwH8bA&R70`Pq}Q@I9iKlTCD^5ekkO{Lg))zCJ&oLcSOsNN3>{mOSQ zeyYzE-?fO`oLgF8F@^8nIl}vN1yLMLZ$%CE&%;wfTHz)fgz4pBX8Gu23iri8s?VL? z7rCge9yNnCf?jk?VbPB|go^bo$h=~3!Cb4Tz}zI$K~6O|-m%kPmepOEJ{KCV5lT|= z`!qE^r+E1FpY!6tL2zs=6AX#ckvD~i+=p!f0PO`Qfo$)w|K3}R{AhCj>;-;s<1Q=f zqh6V6I1&fO9#&w245q$}Q8Qt#e0y2I>4)qf5hg-0uEr{l;fLanYT6Cz79c@-t5Hc^)M8xop{@8UT*VB4RGYT{3X_TJP1;MO!IlRf4>U#Tx?La*4bbrYK z&*=HoCs3O`UWeyO#6ys4F$T?tvfwNIWx82IrJ<@j5_3Ne3 z6#KT$kw(Y;mU;&Wsn5^qhQk)K#S~sW2)ZsGYJ`L`Un)v^hnbVUnv(r1iYa|%eWD7M z(&#AjsXBZ*^{9iqpG%c$M%~&I=PszO`8C0sJsVr~T`tm5N`+OuL|@q$NX)o>+bG}W z;hhIdwM-Qhlgla?m?J;zHT7)EH{$;Z**1U1_1 z+Y2{}|NXgbsv5cVZM;VKzb4_ypYL9>FXC@7)c9_$P;9~9f4)qb0LrlG{k0e2`6d-S z025e${>K`Av%cO~$7U`;cXq%o_rw(*LQ>dXM5rmQI(N*_$0U(D{Xs*B!%pzE^pWmQ zZsPTHHkyrWCtE_r(B{lwvrc^AU$am;D-E3z7yf#2$!0Bpma~BzDk|H6(y?&et&RoS z0{T@OR*g=R?#Sa{=p!Q6UQ0nRM`ZM-$!gnFD;3+j%QCH-sKokGq;`7Y*KIcul z75Xz$MbnL~ZRda`jsR>_Q^OOsh?@E8T*FfPc9~&+>KcwWS3m#!qq$zaR7G#cA3;N_ znR1*}bNoXa-ygF0f=&!K+zP+2Z0~sDRnU*>T)8v;zL-(*$3!`|!B_X%uwh-tN~b}i zK4I0n1#aG~mHx9Pu2r85k-gu{6B^|liFvk{mj@(U>=gE-bR5#Uo|}OJc0hM&tq@+9>RJaZz2}=Mqe#0q^1>Y) z42}I3x%W83vtaoY**jrc6ZW0a1BCl6M_Xj^e1FwSGS#>5Autib-xnA?f70dyw>FkM zz5GEY!{(G+4g7Hsuz5}EbA1|V@qdi%bBVaAulN|Bso$TpNTY4zRjL&!&cad5a4acM zy9iFF#S4iF|E{8f<{IdsVPIWrCN98?3eYZ|ePPj-Pj=j__fM?HWQ8mFe!9qd%L^m< zm@qdNy^gt5;mT`%oma-6da)ly08%xYB}-`r9jvMdm?C zU9)~5O_{WX4%oIQ1O;#h;&Bz+>X-R7LXk5y^-hbIZqErm!vr_r)#S5WtJk_s3i+52 zE4`%)vv#S}sFER%yCl{f2jcnuS2}&io+)HhDx8OvyhH1xmLQ#@r~AWlbO?=`3H7U= zmM!#Q<|)Og7HML9r|()5Z~o>x;r577D5ik*sK56^Aqo_<4qtn*GrNc9HMC&$1CbA8 z`(osnlBmA0!NH_uiuHLWEy; z`Xz=BKmT-va>jjS5XcQ1SMJHW`crSXTRDg#>AsfVgD-^s*|YqM#Yx$v3XAtM2V{5a z$&3R~2=hJYp7f3Cqke6?%xUX_)~kweIA0NFNEZX`cYY%euLzsaEW$i+`e)lY)cX_x zJ;8Tk2o^YY+nfmG!wE`zOHT?1b9_jRE5`?alp6|!DJV{MDhbDw>T5(|5?B>{7Bv0m z6mZL+q%~q3ogk+o0^48twnN$mKCelgU!R)Wxs^pk?; zpMHX4g3c!V*}vww2)Np>@?Qco%UgGz$JE^VdWub{rF1-UL}tD@#R0>&iS{xezD}!* z%rsc=7{s|Ak-;`#FS1wwl(gyhr#pp7N1;K{7gbcD%+_z@NlXmA43SjgQQl|5_M7sVFe^4V>F za6F#5&Gulc-yF&U99s0;_A!zm;pRRrx|B-=9n4OOPkr~JJuv0xgKSj}m`J4QP^w~c5O`|;V$_GF%M zSx&!QCH}03DiV*AN9gG;?gG|;AaQ_@bP##ccUq(4;{EO;d))#@`6ED8u8?cBkee%_ z$KE^nk}mIKj@s#k^BN}U`4@FhWHc&VHq0C*PX3NUEFF8Ms7qQ=9iQy972Do)WKu6CGHOQY=|s-oZXj>6kZl$73>iWLBFdoM;rXzG!C^bgh5)s6c-Uun|9DD^v& zX{osN4A9HfOrcGUcwswmN`KGP58KSyu(>z-hG>g+sTi;w`pI{mVqrk}Zp(T}n7Z%n zIZD9#?71wGqEt>gPY@|7V)ADmIw|*0u zBuZ)07fu5lr_v#(y%~*R{gLlP{7c{gC45|A>47e$wUpv)h{7xt3Bx2g;iVE;GUfU zxUa9Vt|BeWMAbG_<4Q&&&1bRW8h zn_mi|mSkEL3tM2yKz0<(Dr}L)?H2|}RHLO_`uMjdrQl7ua3M2qyP|c2g(X^K@tCyI z@8>~TJ&WlA>VhJ}!K9!q!zzq*X%FS%8i|{s^qT7y7Ti?*vo~7gcAs%WG!~M0k;`nn zDXKn)QE7r)QvE;lMBtBdEA2I|hXgS!wGY+dRs10AedS!&xX65+VJ#)bKSIrS#M5KU z8opG7g`T4a;&KxP>c^ET{8k36;DNw(O&=)aVmMEPXb!?pb5na&MX)SQUm1ZQY1kfYV>piNhy$eOZers&b#2{xWkHb6)E3OyZ8- zzc78NMZE%C8#y$Dl$V+&cQ^|u;b3L?g&ApV?qHHq5zLhx!iJEJ9I%Ef!eR8^Pk1$` zshkz&>=s9;LlN}V_B$B`R`8|q`pg)gwdqy(BXG1^KS1iK=?l(dL)5GURw=V8kHKlr zJ{8_%)o94`^`M8n^j^NT?zw4!TIbPRCUM5nXy1Yu@E=zz9#`TgD-hkX8r?7|wImp? z^E>Ie6258~i8QvvB4gN4(=DUsY3OAR2E-#RaU>GbA-@>YqymW^U6vgI$Hl=q0TK zJg6g6X|<9`+YT#JBqvN5CG~KDK4!x>l7;}E%Iw1iezt2xyl)c{qOq3*vE=8oAlKas zA>R`;NB8yEEX+9suqqv3Y}u`WUq)*bmAK;eQz1#MQ7if`l(A3of-^-~1&7oEpiYPz z8lkqC{`0e!(K9`Un9;tL*moh)jYx6%`fou!+`>Y8p`JP~w8m`mE86#}l{ui&FD& z-e{T`{Y`KvILDVEF+W{MtEIIkF1Gi1SwWEG3+H6r2Aw0So-kJyz@Iu`qJn2e6MD-% zBtl;FmOAKO06xjBplF7R3J?hX6{>6P(sxVXO6uob$g_*{?jtXcWe0C7Eh@h}Ls6~X zVd0MM(^qdwX4_!bEfhHsBHatySRD3gC~!dGUk$hN}A5BO^g{GgHGJxb?FPO>hR zF{OIjTs&nHsbc7bs{)v-bC^SP}{{1XhKxU5^&_eOM+{*%&-N@mj*_uo5 z+XtXMejZ;7GmP&BIT5<#{XWg{Pck4Dxkl|Y#nT}*$|3xcZsV%?>9$jN12qKLM3ZK7 z+;1QeqnxY1! zIL}|ZS0>k5abP9v&Zs%|AWAs@nz!5LTr463KDLl>Y}Zi+))nn6X2&AW-)mHi9ZvHZ zRH%HI9K@PClk=&b?n(C+z5GmhRVTXJf0yBC4Gg7&V*=S7kIhr~hvDkeMT(>AKS)X4n= zXD1hKnojyi4gwGft}LTIe+ynCh$`9&x|XFgRR1aK6ES6)p`t?|>9I_yoWvBj`l`yQ z%#?6YO7k}3B3@pL)gW{mQ5;p|&t=$BIZJJXs+oBOxL|pi5#h-Ll2%nw3g=|x5u6-h zWyloKXTbl1+1Imz5Sd zn^t+c4}9)MvGMmOe{KZJ-4>$7Dt zh1&76_yWLm+yw-R7v)=0>AYiQ1|_$1AQ)cT&9s$dPooO9MeLwY(Ky8QQH_53!gzYF zZgudETDllg(vZse8&J6SRVnkAUqR*lDJcJK=eo8Z|Ca%CUyS{x%c@)l^` zhG5sZ2!*x3Ot#wVk@gNp9UhmHM?NQ+xYd{ChS`IffhUm6+ul27E6~1x~%C=s{oV%>gTLQtsRlOM~=-OH(cA$JZ6ybW6fKB#!N+Z<*0tXao1|m znWLyK`rc)F)OUJ9=YqkmMNOf#v>!+|`gr>t;oXZjGn(0Gwi}XV#X`k9mPjL(vqzy& zE7aMv_=3`Xun)q|oV|m}m?T27nim9)Z9-JoXqj$}kb3ti$G+%$N^vam9lk=r8^a^@ zlzCAfUVFVv6_LB#HAd423b@a$^qUX^oupr`<$`vH{67*MH6R8v{%eL&ch)Hj-4F@n zo9w@Yd2XP}qbvIVph zX4Jc5r{7%fa`F5pSD&S_m(BO%tAelPOH~Iy;1QLAs4jbNs+WCRH{O;4A+mQ1r*(hs zW7$AsNfle0aCAU(d3}E{b{9jF`;s#+FVI@Yms2Csj-VEL*IE*znbV@WkFkqT&f0#v zq+#3kd7ivB!{+7#k_VpOWccw4M_q!Ww-gc#Gyt$|{Y{=86-mSRxlG$+Ua@5rW5c!B zzk$rBLR`z+w{-go1wN6(0wklAg6%9Jc4YU%^<5Wx6K+I7kBLL;YiqT;^BohOOrsvK z9S{_QV?xJ@C0qa;zVGMvjLR{HP-}fQ)E29Ws^b0K$iSStYVj<3<7?Ift^NqfFVjq> z0}_Qwh44!6@+FxE(9CiAuCDepSSLvt>fO2FdVtX0{Q@yK*ztRg-%Q|}zpT{PhIeUf zAmOn1RL(s(&;;~O5JGldVA`sRqgtsRaL<&q?aWf-b)cn0bvxh0*<6$7Z^i#ppk2|b z?&@7YIA$ZeWEyqx^p_Rbd|Yg%va9-wl7_UXAVrW>^8Avs6j%;lv8t0k6GEX-(2{;f zN9E8oIypgHxW%Yi#zEghs76GzNLQahScuMC*FW`XhPvSfwL=xZc^~sm7qAX!WPK%MQ_SHx^J2eTCFXXYIHfg z@?1Q_)M`&x)f`9iS`DB5wVkt*?NPEnR2et2+^!)cor!D+;hp4(~5Wd{K2?ck31t{OJ zrc<9K6M=sL+~H`E^eJ9c>og=77RR}0N-H6m9V0m_8EzYo)P`JBS~acjT}Fy81kJwQ**UNkj*2WBO}tRCyQ%Uz zf|GkPzuh4gViI>uJizRpu=vtx)F(vpGuJWY9VeWfFj7TK7ab)vunCDtKuw{Y;TJ-# z4)me(A^f<)VfClET%*H0Bp7n47O^cbIYeYoSq95S^AJ7l;q z>r}Axz~=iGg1}?zcxMue>zD9J|Sfj8R$dNt{o>_2dh`3{VZqG^Fia5DlJ;_Tjw~JS`V40Qdj; zBZtcZY(R-hh$t~YN%Cou@{9cf=K)}4z@S&BwC_p}T>d!{$|iVLJM{V--HC|hMJGsV z6App&sW?Ef$F;Wc8^LL$;6h3~A*hcM5Ke@8&i~H-0=~VtN7kixtZnjOhQOL%`Si?; zDCi#t4IV6U?yqc5dAz+A#X~CYRU1V^Qw~G4x<3E#C(zlFH5sidfJgWE;_iA<0J36o zZf-5}*BT@J8O=(SGEFrppJ)9e}rVcVLzNs9baw{E%d$CI|+!6O=~zPv|uj-s`vXMwll2OeT>J9 z7CUsDaeKgqZb+V`DEZw5o~;SD{LaIo<^ zgAopSFKVMk0F{UdGibno7C3u%2TWkMm^4>7=@q>%&HIc-xayp2WrWn3jw;|QPi^qj3y`!xiBlrHv7{Sn~JC9FUo>4qsdu|7qSBFUJ;Bl+KiII$< zY1-Y|(C11$e)q^WS%Q&LA6=cyh>6cq@n8NaQpYE7R`;FsVSiu3iE~+6SO3n?c(GGg`|VQ7xnHgOh**TAex9LDf$sL1obR8~`j_DmJN6lSKzZ>p zABtfi!u2>K2Z2LE(;S&8Pr1 zXltoC%bH4_P7)QS?XMc7et(3l5!!P0K%2nxH{L4^n$?ctRPWEyzxpkpPxZSg&m=G= z(!aDsDPY4GpLyul2k$Z1%2lFW+k`GOkBN`sZ_;~l_4>6yFm;QgPoGlvr-Iw|7wQ6( zjGc#}iA-3kpS(X>DvJ{)4-(&XlZb}jH=h< zAIIM1o%8?$g2#z;1K+*Qf7rX(Qqbb-z~0Q~L8$> z1|KYQGGMmvr3u=;j_pR!w?yi`6<9(2>ycJ!H9HcU=_HFWF)J{4p~=Us;ZD?=CPC4XLpfpe(tlXbJrR;+PZ94ay5SBt>H9Gh$SYcQ3|it17rG z7-S*XO$-`#{}~cCxPlQ;gIDL84$2DGffFiT zs0I$lW)FDpnrH!%Mqb7mVbYZ3#%Q3=p(u}psbL<7l^2T`5xA;dx>8I^--JwzOb8(F zgwOR=@B%CyO<1S~z`}_b7|zV+Erokw2#KuRoJc_ree#b2K{@WiAC=}5IG4h5EtpnC zk$CiAD|#KTwJNCeC~x0z(kZst+h>6zPz#c+8hpuNovcn{#pJaVS(ozwptz^*{I&FT z->D<80f0xeG$$$fxcVUhYv(#&Hh82NU*#r@c@w07pOBCs>4%;%C^T1*zn%IE0~>Jv z#@PhaOAGE*-}w6GiXpRnOiD9$;nmCv%r(xw@KpFpz?jO-r-g<^mQo`%4efT6EU{ol z2xbAeVGa)uO-Bz+C3QwE%lV*G!_(dYByC@Jdd@9Oat>Gi> zf-@!9RW0;_=pP%lwVP}xUYE5NMMF>)n=Zf~sZpo2d9kXL*+?_w!OXC)$4YY~ViQk$ zB+US3Np)nrK3gteX3M4gRyLhHQw;l+r(Wf<&RMYkgN!l6fNjN*XQkvsh>2NqG-4j{ zra5g=p>k)l`_=Kjm?`baegMb1sU`6f*Rb<2tcpSU`s80H)e2YGXl^N+#nKY|g`@G9 zMb4h))ZJ%s1{5aZaRZK?V_(iwI-+GlRst1rU=rxq_qkknKtswBZ(+QhLG+ta;e`t+ z#KOW7u%7cN)UXn`TYAMF>h)l1h{3=yR7U;*CEC{SSAFJ{om-czs{(6h@Sn~psuZ+7 zZ-PL0jc#4P@}O+TaB192<9>tbmqO$j3c5k)cwJ(IhM?caeS|DTu>bv`1O{of31}0e z)zwwa7V_JFv<_ji=eScct{PAp&+u?Jtj%zLiR#ES;Uqlu8Y%P712y%LM^LdWdR+@~ zYIe6_L5eWZtSh%?Z7)^PYQ~DDg$ZDQ2-WtXWg;BB`KV9rSBFjpYDX)j1EJ?CcYcGz zY61GokObv?xhFyh^bvc$39>)st9a^ULsrX1(8lnKh|ti`TS3r+NWX%}`?f7>cO>gB zotrVZ>+;6X`4KAjhrL??mffR-YQIvIpDAo`(Kuzpr8da5Z^N`IFSz=ndX)s}=lvV7 z(X}=7i-7s9)D;;6Y`Dh;Oc9^tmC@ftpa2-sdDkSxn(-#X8=Mj*<(fs1@f%{?>ew?hJsn1Q1m2 zQy6m1Duikx8DM#VWD;$*$7e!JvaW`j@jdDfKpX6G+fp6j*@>G?Ni=NRyb9i0DKWBd?5t1zc!wSCUWHfvPe8ml0i~KG%0KE$Hc9? zn|N@+yGkq5ZlZeHqXcd_D2p*BQ}0{eotBdL(+6&J4x1vJYw-Vzw24I6bkV| zF6H#um3}|*D(&t4xAb#6d~QQGGdva7#=NspB*Zero}{n~~Q=~#$sZS94SQ3KjLpVk@t z=3wrQEW65tNFOy8T56lLRZ-@Cy17Yccyet%#kXy|tNyv(Vs}=It(WYTfg`9YsCt@c z7wnWoE_L_H&{JE>8bQ1_9w9}_y()V}I%}Aq?EOtEwBYXOeW4|-Ln590+8S{~S&C|o zS!BX!f}JU3L5)0T+4n+-?}9paiIu=lfvI4`Mx%&2C&BS2uLK*tFLwF3*<7M}%Pczq zv1x%#WcF2_N!mI7&HtdHG!k5t;STCoXEZ|cW75YTg*D&2Ni2^#nsr%DP^X+`81*n& z2j9L9XrKGLt`r>n-L_x;k}1Tl8s<$-B)@K-*fL)u=CJu?`GXXJ3nyXro6=$3cXG2^ zo=b}~VJpF(X{}s@7lQtul`nUL_)KkzZQSxv3}67$oq%XIMS@hNO5fraE+s=J zIuc5-Zsq}BmD!|^iie~`dB&_ZU9O0H!0A(yA?)0bQ(0RJjXz09a@*(S1&J`v^asWG z6)XJ>!Z*I`ta-$QuD|qSo=1*QL2>;w;w4U=FGLyXwB(H3)p#jiNq)qJ7N{Eq6`Sw%f)wJ|vX;V(ag?MBz^$ zIyzYX2%!N4yxnRd-JyE>#=T!8^hWoQD-8hnfqqX z{u*UaQ~jvqwI{+|T})*l8S&XoP#7!3G2nm6Afrz~>%RJ%+}L48c15_Us#fv1VF5OQ zX^k-doq@RaPtOgm)6#*TFQqqQG+Olqn5B}w{EdP13x&w8<*yYl=m(oYb1D!~5_;L) zua<=mH@c1LL`y$TewC)%Cnrn6b;k&%fme-X@g=YHw!fBh?Kc#>QDsJf6q;1HF3L=- zoida@UA>3U=(wf`W1Ujq%yP_856HzOuCN*`_lJ?m3yIC25r7CX+QzE3JQ#aThQb@& zWrvt@^ZOQv4+!X`;nVidMY7u(0Jdj{`)JtEl4?C;SCnU%<(CqD+ZsL`tnZq1$cTJ7 zL2h)+Ja|AvxU5P`#yB;0TbYB%*~xv&!k%f1;}ar2kxShor|3)!U)R0>{D;@!LiIHlLabP`%b|LK(DumO#pm$=HdUS*0X!ygZ)>2S?z+xam^X>Q( z8hdqp-@KmS8ig*Z|9cl*_i_*5y|Uu=8w5;Yff#oFKLBdpaxe zY6gbvr-Sh3nrqy^UQeWHIt*$MGbDdaxMQQF=5hg6l~x5Eby+?N8|pt(bNE4x&;yMp zg|;TvypN}4Y)E0`&2s6Clcb+cSieSC3Yl8d6&pvDw%m!Y?1CU}fb+h17W2}Asecfc zcto?`c4+yj)qX;J&?t8zN4MA}^LaHQ`ZHM}w?r~vzj6X~p=2zuvno%pfTOk1eq9f1 zW%m;?lO>LJ-P^Tdr$%D+bB&|u9W1I6!zQpGZHB0|m|EKTR_+RgmH=kOLJ_t^^vYPL zb(aKZmrnOefQ=fJb<3HZkW?JK?WC5dh9sFW1mdKkfLY$dM4(OiU}u$?s}NqK-UC6o?(V8PCyXFy zL2#so!u?VPLTz_LaHL%AL&V~Kq#!D)&_Ooeu1kUy=rn=D4bjY;jC8(qZrylYn#|R% zd?r)1a6juqvayLQ`CT?%-XC?H^*oG*Y)*ir3}X7_bX3gHtjvShw2WN4Qxy^&Dbl?r zKiR!7YiX3u1k>~>{2DG1WxK5L97{4!i~oy>_Q=ca^1sT|f20I2b3*quzjm8UIs~&> z#9bJbbupJ&p7M{uaB};|Rw_~Jh{3@hYn1MUT8uwLE*;bA-%ay&9xqRKFhTx7mGb(e zpcOM~Q^gB-ABV4$Gml37LYnze96vR0VSYGXl~=Qa4lc2}?+=jyI8qm2d^P7}UN&G# zy?l33KQ_go4^S~zr^qlp;kt~d`+EsEYAc&$`5gM=bTN|bNG~mb?x>|qC*}>&p(Ba$ zEQ?;Fc0RQrnR14h>l%qJ+6}@3rtIXp=jTJf%L0rA`KtZX{WtVIQ#3nVmF3DI>3}w& z=>3;fA^jULqMrj+e(MW~@=~US5~V7p)F-y%vcL;E2gj6-X-W%fL+r-n)C@ENuy7db zB*?$Y@~ySCr@-6`lx?HWrs)uGNAFLgjM@JN33*XTG#vJ3a(c3hxw4Gib+w}}U}s^( zP9~m5t}Wg9=IXweu)f>vutGn>CYyg_^ zI4FqdZ)`Q)&OI1j55dlt-ncIEGq^5{klG=qTL?qtHo9eAk&Cw2VFaDD;0b(G<409| zG;3G{8lZTB?BPrbjA{E@W#N+}gDjE>@Nz@j761Tg0^7ajRLM2$*yvl?MD2G|ToBn- zbiRnq+iq2%0DJE|jTlZU59a*efbxPc3*qqiq)IQ(t?1HVK0hxt;T^mm;>E{UG6qV_ z2&{Mh+_tRXPeTSQ1JpU7aO`lPcdi0uo7?~2{t2M<{QdQR{rTrci;?>XlF9+u^6q)f zmPLkQ@zEyBZf_X3>@VL4!m-$i^F%aRPW9NF-37y8eiQS@5j z3e4~jiMwfvGVhyzQdi^wdg6$laU@!W0xH+`h`sb6r1kZ9Bz9@Enc8#`6sB!&#{fTS zE|~Mr>?T3MQg8#Ky+gmGyl_qwW!<)P4Zt$v@RTOI_2GdEx1leTrF1pmN|WR(6aW(g z&6L^)pw^07x3&JRQ5&JLemCHajL#^*CL)m*f2aQ540;BU`ydv}izFyVbNy%BpCES=2mNRAag7UxEWUs$a?RCek%5$w!xut>G+${=LV;9*FC)Yymfj+;=;5$6Ou! zc`xZ_3KhAMe$nJN<77~+RU@jOcf=E+Y#TJjC^-4EF&VoTL5;xkr;atumEm|tp&@1c zOmX7U(FB0J$#D{0dm_IFJ-hN(?32@XJ2X7rWwWZE zb)~o3+sQBGn76`}lpg};Lb!9oet{T|3o*l1(W2T=rM3U*+vb)PJ1<{Q^<{HRZZCm6 z)4F1NN+remTa0uI!g((v0_?XJ6 zVf;%(f;((GFMkAL%lZFN@w&eF|EEfQ-zNanDZpe!YPtSuJXZ#Y>@gZ8x4&u@(5!E` zxfRN>{ng!*f(+7Ng8x=M8Tt7{!hdwB0Mou5`|!M60VFUK<~9Ixtf}R#Xawjh8m}8G z=hFMY90|fMOU7Y%DPdM!${=CBq6h)Z+pyf(=mPM%mu3C(Kv5+xC;@s>0TM>niX6Lg zMi>c1h9c}6zp&Ed4Z3MbB=l5zSb2Qol-oBb8UYcM58hJCVX( z5YuBjMgk?r*91$B-olK60^iZOff}DhS-*bMM__qnI0;g_j{cE)mji^Y3JGB(2d-K( z0^`D+8JqocjcfR}dL*pIu@Tgas`GglsDaX&9cy?^W4Al4-#3M9r9Z4_3XxO&AK$Fg z+q{VCNzBUlQ~Lo4ch<$2-7|RU3QMRo~Dx&do^xYLgOi^{<}Flng0}aSO%( z{)ghHV}}(r{4h44>ha7$6et6*xk-H4u3+pX^I_lL!z(op0q+Gm%gBkO2Y;3zlG0lv{}zz0R|9yd#fqyBz=1E%__K|LuMJ$D{ei)0-HkMDZbm!Ks)`+^+2LYFR!Tn4+Hnia=R0hdBH+QQk6b}N9mal ztWm_?bb~m6+2MNCbL;y*&;EIAHIWt6cuHWAgtVc6>j6;Pf)c3W;J|?^0xbprH4&jM zhj1DgwryoZ5rJ|H!hm69y? + + + diff --git a/docs/high-availability/kube-apiserver.manifest b/docs/high-availability/kube-apiserver.manifest new file mode 100644 index 0000000000000..66b3a814f7a9d --- /dev/null +++ b/docs/high-availability/kube-apiserver.manifest @@ -0,0 +1,103 @@ + { +"apiVersion": "v1beta3", +"kind": "Pod", +"metadata": {"name":"kube-apiserver"}, +"spec":{ +"hostNetwork": true, +"containers":[ + { + "name": "kube-apiserver", + "image": "gcr.io/google_containers/kube-apiserver:9680e782e08a1a1c94c656190011bd02", + "command": [ + "/bin/sh", + "-c", + "/usr/local/bin/kube-apiserver --address=127.0.0.1 --etcd_servers=http://127.0.0.1:4001 --cloud_provider=gce --admission_control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota --service-cluster-ip-range=10.0.0.0/16 --client_ca_file=/srv/kubernetes/ca.crt --basic_auth_file=/srv/kubernetes/basic_auth.csv --cluster_name=e2e-test-bburns --tls_cert_file=/srv/kubernetes/server.cert --tls_private_key_file=/srv/kubernetes/server.key --secure_port=443 --token_auth_file=/srv/kubernetes/known_tokens.csv --v=2 --allow_privileged=False 1>>/var/log/kube-apiserver.log 2>&1" + ], + "ports":[ + { "name": "https", + "containerPort": 443, + "hostPort": 443},{ + "name": "http", + "containerPort": 7080, + "hostPort": 7080},{ + "name": "local", + "containerPort": 8080, + "hostPort": 8080} + ], + "volumeMounts": [ + { "name": "srvkube", + "mountPath": "/srv/kubernetes", + "readOnly": true}, + { "name": "logfile", + "mountPath": "/var/log/kube-apiserver.log", + "readOnly": false}, + { "name": "etcssl", + "mountPath": "/etc/ssl", + "readOnly": true}, + { "name": "usrsharessl", + "mountPath": "/usr/share/ssl", + "readOnly": true}, + { "name": "varssl", + "mountPath": "/var/ssl", + "readOnly": true}, + { "name": "usrssl", + "mountPath": "/usr/ssl", + "readOnly": true}, + { "name": "usrlibssl", + "mountPath": "/usr/lib/ssl", + "readOnly": true}, + { "name": "usrlocalopenssl", + "mountPath": "/usr/local/openssl", + "readOnly": true}, + { "name": "etcopenssl", + "mountPath": "/etc/openssl", + "readOnly": true}, + { "name": "etcpkitls", + "mountPath": "/etc/pki/tls", + "readOnly": true} + ] + } +], +"volumes":[ + { "name": "srvkube", + "hostPath": { + "path": "/srv/kubernetes"} + }, + { "name": "logfile", + "hostPath": { + "path": "/var/log/kube-apiserver.log"} + }, + { "name": "etcssl", + "hostPath": { + "path": "/etc/ssl"} + }, + { "name": "usrsharessl", + "hostPath": { + "path": "/usr/share/ssl"} + }, + { "name": "varssl", + "hostPath": { + "path": "/var/ssl"} + }, + { "name": "usrssl", + "hostPath": { + "path": "/usr/ssl"} + }, + { "name": "usrlibssl", + "hostPath": { + "path": "/usr/lib/ssl"} + }, + { "name": "usrlocalopenssl", + "hostPath": { + "path": "/usr/local/openssl"} + }, + { "name": "etcopenssl", + "hostPath": { + "path": "/etc/openssl"} + }, + { "name": "etcpkitls", + "hostPath": { + "path": "/etc/pki/tls"} + } +] +}} diff --git a/docs/high-availability/kube-controller-manager.manifest b/docs/high-availability/kube-controller-manager.manifest new file mode 100644 index 0000000000000..f66b5824de386 --- /dev/null +++ b/docs/high-availability/kube-controller-manager.manifest @@ -0,0 +1,100 @@ +{ +"apiVersion": "v1beta3", +"kind": "Pod", +"metadata": {"name":"kube-controller-manager"}, +"spec":{ +"hostNetwork": true, +"containers":[ + { + "name": "kube-controller-manager", + "image": "gcr.io/google_containers/kube-controller-manager:fda24638d51a48baa13c35337fcd4793", + "command": [ + "/bin/sh", + "-c", + "/usr/local/bin/kube-controller-manager --master=127.0.0.1:8080 --cluster_name=e2e-test-bburns --cluster-cidr=10.245.0.0/16 --allocate-node-cidrs=true --cloud_provider=gce --service_account_private_key_file=/srv/kubernetes/server.key --v=2 1>>/var/log/kube-controller-manager.log 2>&1" + ], + "livenessProbe": { + "httpGet": { + "path": "/healthz", + "port": 10252 + }, + "initialDelaySeconds": 15, + "timeoutSeconds": 1 + }, + "volumeMounts": [ + { "name": "srvkube", + "mountPath": "/srv/kubernetes", + "readOnly": true}, + { "name": "logfile", + "mountPath": "/var/log/kube-controller-manager.log", + "readOnly": false}, + { "name": "etcssl", + "mountPath": "/etc/ssl", + "readOnly": true}, + { "name": "usrsharessl", + "mountPath": "/usr/share/ssl", + "readOnly": true}, + { "name": "varssl", + "mountPath": "/var/ssl", + "readOnly": true}, + { "name": "usrssl", + "mountPath": "/usr/ssl", + "readOnly": true}, + { "name": "usrlibssl", + "mountPath": "/usr/lib/ssl", + "readOnly": true}, + { "name": "usrlocalopenssl", + "mountPath": "/usr/local/openssl", + "readOnly": true}, + { "name": "etcopenssl", + "mountPath": "/etc/openssl", + "readOnly": true}, + { "name": "etcpkitls", + "mountPath": "/etc/pki/tls", + "readOnly": true} + ] + } +], +"volumes":[ + { "name": "srvkube", + "hostPath": { + "path": "/srv/kubernetes"} + }, + { "name": "logfile", + "hostPath": { + "path": "/var/log/kube-controller-manager.log"} + }, + { "name": "etcssl", + "hostPath": { + "path": "/etc/ssl"} + }, + { "name": "usrsharessl", + "hostPath": { + "path": "/usr/share/ssl"} + }, + { "name": "varssl", + "hostPath": { + "path": "/var/ssl"} + }, + { "name": "usrssl", + "hostPath": { + "path": "/usr/ssl"} + }, + { "name": "usrlibssl", + "hostPath": { + "path": "/usr/lib/ssl"} + }, + { "name": "usrlocalopenssl", + "hostPath": { + "path": "/usr/local/openssl"} + }, + { "name": "etcopenssl", + "hostPath": { + "path": "/etc/openssl"} + }, + { "name": "etcpkitls", + "hostPath": { + "path": "/etc/pki/tls"} + } +] +}} \ No newline at end of file diff --git a/docs/high-availability/kube-scheduler.manifest b/docs/high-availability/kube-scheduler.manifest new file mode 100644 index 0000000000000..686fa1b20d794 --- /dev/null +++ b/docs/high-availability/kube-scheduler.manifest @@ -0,0 +1,39 @@ +{ +"apiVersion": "v1beta3", +"kind": "Pod", +"metadata": {"name":"kube-scheduler"}, +"spec":{ +"hostNetwork": true, +"containers":[ + { + "name": "kube-scheduler", + "image": "gcr.io/google_containers/kube-scheduler:34d0b8f8b31e27937327961528739bc9", + "command": [ + "/bin/sh", + "-c", + "/usr/local/bin/kube-scheduler --master=127.0.0.1:8080 --v=2 1>>/var/log/kube-scheduler.log 2>&1" + ], + "livenessProbe": { + "httpGet": { + "path": "/healthz", + "port": 10251 + }, + "initialDelaySeconds": 15, + "timeoutSeconds": 1 + }, + "volumeMounts": [ + { + "name": "logfile", + "mountPath": "/var/log/kube-scheduler.log", + "readOnly": false + } + ] + } +], +"volumes":[ + { "name": "logfile", + "hostPath": { + "path": "/var/log/kube-scheduler.log"} + } +] +}} \ No newline at end of file diff --git a/docs/high-availability/podmaster.manifest b/docs/high-availability/podmaster.manifest new file mode 100644 index 0000000000000..8fb13b5911ab4 --- /dev/null +++ b/docs/high-availability/podmaster.manifest @@ -0,0 +1,57 @@ +{ +"apiVersion": "v1beta3", +"kind": "Pod", +"metadata": {"name":"scheduler-master"}, +"spec":{ +"hostNetwork": true, +"containers":[ + { + "name": "scheduler-elector", + "image": "gcr.io/google_containers/podmaster:1.1", + "command": [ + "/podmaster", + "--etcd-servers=http://127.0.0.1:4001", + "--key=scheduler", + "--source-file=/kubernetes/kube-scheduler.manifest", + "--dest-file=/manifests/kube-scheduler.manifest" + ], + "volumeMounts": [ + { "name": "k8s", + "mountPath": "/kubernetes", + "readOnly": true}, + { "name": "manifests", + "mountPath": "/manifests", + "readOnly": false} + ] + }, + { + "name": "controller-manager-elector", + "image": "gcr.io/google_containers/podmaster:1.1", + "command": [ + "/podmaster", + "--etcd-servers=http://127.0.0.1:4001", + "--key=controller", + "--source-file=/kubernetes/kube-controller-manager.manifest", + "--dest-file=/manifests/kube-controller-manager.manifest" + ], + "volumeMounts": [ + { "name": "k8s", + "mountPath": "/kubernetes", + "readOnly": true}, + { "name": "manifests", + "mountPath": "/manifests", + "readOnly": false} + ] + } +], +"volumes":[ + { "name": "k8s", + "hostPath": { + "path": "/srv/kubernetes"} + }, +{ "name": "manifests", + "hostPath": { + "path": "/etc/kubernetes/manifests"} + } +] +}}