i{F. 節t,Ryiqd8 7fD $H@Iy}%EjUk?+bͳg3.i8ک\~];U[nl)oh)!z7$]k7O9+Uq*Wnug\l/r$A]OҮ_V[ l(/GK!{#X&vB\_W9)okyj]SS5qW] oWsAZ䞼AZO~)< 7}T>x,.M™Ym\ӸT3JǺtLi%Nʶb/>W]Ǯ0WY$נJqX|Om~ϟ^>Tx4_ހr } ~s]eVhև#[m7A|?}>4|xy~zz^ZoJzZ|B3'o_Z7^퓗?ek|!l_'gM+yjI7hnj |v6`ΣwEQ&׋G7MƯiDBijJbYz^ɫgfykPݽv0uwW'|I‘vvl6 >_}u<-|iZ_2~5,ÿq#|xEXo[}ۛij?_5>Y_im%ׯe/X؏k_^o˛@ͼ7?py7oDϟ?uQW=X[ԛoσAQ6V)7)e;I@OA' nod)?"ap,p#:(\צO|J9ѓpa_/߿]۠l2h)wt__/Q\U7Vzp_ȕu34+(`~~ N3pK~ hokMY΍io_o?DdDzۘ.nO(TUDPמh]›|HoOC~>wխԋ]SU&W W,mG {'_1}ѧݾoP[Ho_K3z|]wg~KV6rr\Ewp퇏yaDDOOo?Ձ𥂛rҊ.k-&m:'GT:T^cݓʂM!$6xcD ×ɿӭh.ۛϷ]$~k4x܌O~.-Tˏo$~>ib/h>-k{K)^R_fyK׏7A+d{qׯTۓU7]^: <]VNM5*6L"Lb"l/xZ>ۏayy[_^KDrwAR_&ɷo( ת)_~|;|l|&_~O屺]{P)/e W_3~U2ߟ?G}=CoAܔ7-) }#>{7.n军hZG~~}LJ'oߣ\% @]ؿ,_~Ŗ~y kg|`~.Fm0H?X)4_נ%rKAO^H?8^Da1?Tp9D$}W8٣Nßz6]w1z ٠kH-V_7^?~|  6kܾrqO/s;|K_Bk_9$ߟo^/O?痿OP6sd.VJz?1՚| ۷oQ%B/oW ^[?~<|(~kGȴ@e߀syޗU8)^<={ti7n eqṕolkPp:} `ZS̾)Qk@مgD3[Hsp{x د~?W6?+Nu͛W]=#R3篊7#~y~3~GN{ԯ-\Es=<7F3:"tdz ̯d~ nG~u7@ !O:_~`@?@ .cۛ@M-tu {an ؓ7K4b /!R_YnR[GDl7I1qdH!j^@%XA?͛~3[dm}F֭U<._j7OqBFH+ҡC9ᯗQg>~G80-2x $ؗϏAm@'䢉#^^o_‹\߫_-uΘD׍|0?"hxo= ȲN]/@eON'Y3(7c!9¼˷?i?ޜ?a/P{z˷oXȭa7'"V?_EWq—7+/_܄-FO=y3+r|=yI'c>o2&!{7No} B_YojTXo}wP_@O MYs$ 틯0an{s;춸r9:臘Et1cය?XqHOӃmp#jO [szyo/؏?~_> ~z ^yP+٧'+R`) .ۦc!Hq/{AY09'D\F^0)@o5W3`?[z .OMGooUH ^y N&^ݽr}@Sc&_̛e$ոiTY3[[S3@Znjo~<@tipnU*C_|,fu/o??)U Lq}|({FC?P_"xA @<0%0ݿeB>~A8)^" (V@ Dճ[ .t_6m Y`={޽u}ef!XN+<}|5"|Q>#pOB*9>`bQBc(g γ RZ3VРp {:3EaTNژ3| D.xTƉBͣ7t|?_ү2y5cඁ|3jq=VO_ӯ~_K"Oo&m" c+ï>a~&o_+8}~z&܂~?_߂z~yz6+isXG6=+/`-c ~ʯ߀_9.`o~?ƀݷkءG_!=8]W?g g~{ ~i_ruJoYegs,fKVwE^]˹i<*^]'Y|<(z_0F{hb~h`o \N~Mk#Klջn6j㵊W4wymDUhIGKJtOmdS8eõb4pTHpثfM)U$yJWk+DWئIjI}`2s+h9g_vUgp:xݬfq{ã).@^~3NF"FzFϳon߹gA>Jvm|^!5a`絿>qR*xi˼e -@}fBMA(UOq&NZN[  zzÛW5J`*P_kQeOx_(lټNk8JdzXf8UOI1G3!/.fn`ۓt aǚxA'`ZbT6Ev'Yn"Jt8`;I|K83;'oֲd]64/ ! 'AY\ۦ*ڦ߁{/w7~v7?Î<.~{Wo?/ծ-VRrdC>j<2K~hDxʋ8 'ݭ``k9MW綷cݱ>8NW*MU\&]Iq<+vU~&;.<&~cZEyE(. K1LsHºd#jMRͧǻү*A۠Ks(}pk@1\P*~G".LKpHS71[m"Y*J0{9h5 mc@ ٟ8bHgcĹvJZz;q N!B%C \h+Bz~>{HTw)Ɋ-ð[`&^h2'otlwd :pd^'oj6VR;"XV]Cb1NJXR#Q2#T8g! v@%=N5Uƌ:tMA>\ϣX }${$2]UĆ+&G+a܌uO,^3PuM$v&k*~kh'P@ZPuzՆL!/EУIVxuT~@mC m\KorxmN.c;aw/`&Ww4!=/"D o H.@pL}eu3Fp%56Z ֹ_~5cNN &5I61}69+pL6I5$S(_6~!F*vkYމd ϊN`{z rs`q8}kc ,6/k|U2My-߾׃jt2Љ>vV 'n9aVȑ0;GQ:ޙwUd6[ӭj6Նa}jD7W·.4U7Ppº =0/ 2Eȥ} ,EV넬~b`EHV7<:cSmYy1yY:t8Bh…jҐ;9cˣ4ÚGl\%y(IyYp [ax%KLݸetJKc ]*H{ЛS; , >T[X/ٌ=\y #rg/@I8$Gw ڣ~(Uj"HH~Mր4@›IKn}*cbv۬KMg, +I!m\H2;~BaT1xq\qGE*O˰I f}̛y V(僵nW&0U$kךoJzb\wW l|“_d8P epN(]h9d+1!pǷ <ÃM8hd**BxqryPXzw[[tuD5ॅGalxRq}PgYd** ҫJ,-uNΌ|׎*<"dnGN_;Dy,s-S[;lQ5[a`Rdqg0;W+(ymsK8f-%b\äs"\XS U  Mm9+2%TjrLS~5Z_HK dej uѫuFlL녹qfL::9NDukQ5'e6lFs 00 NpLiz O]SiN]Jvͯ,9 uԤNfljyIw9s2r]iQS4 68ϳ.sb b)se.wte,af s*:ނP;cMĎ`K ǚP,{Ep_oB0}J?~w~jЯ [5onSe[yGi }E"wQ5`{Iϡ[ yaNu9ZMȮ"Ah`3/O%Aiܭ_S5B|&)(vtr6>UIߣɵ?eiycE !kYfND :8ch'E}dX+a{C m1(^Uv#5w˻k5c ՘1,)#.vkpU;C?1g.x$!WDvSQS #ҍ6>`L d L2S +|vͦX:Lg 8& `iCZ]ױByhmwIԙuݚC;xD.B %̓fōJו%!&o!yw!וOPN]z$uA mp\iN:ڂ_NL Ղ{(/UҠ%G鎗L] X8 -ԈBWwy#w<徣L*&P7n0tILwQQ`yMFlp2$9y,G_w?*V?j;l *1y~i@8y{Qmwm<p!t&cnuw[D[ ~Zd/J޿Ɔ7A/rP8=a}{7وq. s6 Rgll 8C!";1vqyq%ާ| /sg6[EKNF|kRc ik}}(~ Z*(Ek֩HK26S"gna *=Y0` 8h]+0>EV0TO0np#PniNOj`3N"= :u!"`I@*Ǐ :'76| "O0 !'/~_h*F'%2/*qD($ Ϙ;t踗A@׬4'-TќASu~I)"Gn\D~yU5~2oMX&<{TRSG5:0nX#dCzRf+16~V7e%&k:\REYe~UűPKzmܘ/ZvNSEĸ,~ ^9ËюL!B܆'cwݢA8DɅH L4o =odN.b1rkZ@gη~FMg˸vG~SׅhP~ǃKSO Uh u#o\lSZ5/ $* C ($3 q@O\^:7xsprG<xW$ƅVY9bɁ̄):眇 m FdIE4m/8+p{vm1[ N kWuP*;49^ŤhnV5I+ֆ{O t.3A⠜&Fdp<#[Q]N݇4QG ׻/ŎZ2IK'U e BT̾ @{cڲԿ ?3te0a 1",R0KHF-׋;gRRCY?~H$ )@qJ:0z:Rܱ{ԥHwM-M.^_;Rr}F GL_1v4. J!dkć eDCb _\Ithc霉WIl <`sd=̫ח="kr|NSe;|+u~}rU_bRxPo҆*q^V~rCzJcQ}V~йtwz2^ൽ=agp 63a^hs.7D0ܗZ~F六JЪw$m(yl*3*5򠷁/ingn ` ٨d7.] \!_:|r5* ΖOW~7oic0.>*WբNdꄩy$] %3SnԗSN# Lq.ı`#@ Iݸvy@~Jצ'h5(Tk̳Bjx zV"[%l/nF4-hvFo5%?v67Ac`C屘f@311j=}-H3x d`);dyʑ=RrƛEsfq;4Bͱ񭝣m<^/f~ij(֎_t.㔛4x }^Ő| _A˞&%ۀ[O\@BL#rz{L]N4IϮ:7TRC!Y:&vyܐ D䈉')5M|wZ^' /WQa%Tb W:/ Y!)p kzk9 #Bk#a$1Q.sk2o499Z+k'r>oyT qF@G&E|1?7W(H&n`TTwJ(Dʫ'sEr_;g3cA 8 lh`LT:˧٨QePwo.罪 J ;ѡXg~[Ż1=&ĺ8 TJIVFPgbg$jp ZB"-ukZS Has7;H蕄@ȺbjOGp=9(Hnsy\ (/{Tx,Ϳo߼}۷.R<04윁zev܏3U||pa`Ry 7jL_AGv^}}nJIm 1ZϞHu4ϰX-0zlk!kμVw`vʝI@OJ{Ռ E dsJ80[}!s72w1ԒNb) RWEO? O;EZ^Sz2-]*Xķ䁖 ];2q.`ME@0^C*ZժFjj,0+Ăta{1 n|M ׬ChQzxo!=tϰ%:iyc]](䆀FVL(+ *㰩N}Q's @{=I|^By^ISE0&lЏe:59GgEWl6 !&=Į t )k|q;%a(nnYu|",{kfڮA/:b"kM|$i`lm&0wP-lEb#f RgGaU '9<塠mQwġP(]򍐒٘+Lʔבꬮ`a@Bl M N @CeDppϼhzp JxN|?ھrH'Jy"%&>Fu/h9nnMs` juY0{b "q]82W9¨^_EpP$3%1 On0ΠrC=aulX$hZd&{/3s`!I5Ho <顝4b^ZfQ woe%2#anfQ.6q1J 2Z U ^Kԍ+YEh0xİdӰ()Rzk;6(W}ȠFcy$L W̥eq_{L>Aq)}ETLv; zw*o">mudf B$d&զ!͎}.ۈl;'=>xN5UjvZ(+b/opN`q)hM>R; RKvʢ bS ԝU{HuS&5 1vƴS Q#do{J X'f l.H0cdRَ#^\sW~}1Cuia4%W7j?Qt0~|vN9; 麍VAޱAȯ}&~41p?=030U.`0? *&cz6I&řwZ͓G=wAB4y&w7vЙnHH4Pb!"O8qW632f.6sb?pS0 Cg O83LlxlQf}X`3*+?C:9qUBSswux0[QmgHK@r{GR\+͜XV;MTֺz6v$`k]K"94lIpr"9oQ- 0 Nmlg .Z4<;R?kc ֱvb8rlmuQ*\xyKDXq 4͏jQzd űȕ@tT g Kt;]ƿy*vJ< Mנ'jл܊@ag4YPhYx<&wal yNQ!eiMy51z~xY +ˎwQ:4>y(PFoʿ2!c~;c_$AX)AȻTa"Xb*d݋"oW@P.%PKR1Q>'kzCh§{?b)ze!h3nbd>p} <uy*i$ a zu=OW2- p2уު+Π;U,@4HÆ )bP~8ǮГ+`F0o0Gg^ r0Cݔj&iH)L!| u&qxJ 9H)DIZC\ϋf[%9eV]%tDl{݆>p> sCHu! WQ7c 9BS;t\>,T=X[D;0§eGuI{" =R?\)M: BZk&E D:myҤYEeaƆ]2t^;;5hĝL8:jbl;s&`H4o*–yP+zmꧠNw'7t=wz#'锭5.Tz(`˻S$GK٢t:,`|(v>73ux\ׇsp6c3'o|G^'c'pOG:q(-pz~wJ$Q' 9'-腀DNwpr5ƑQN0DH \睤5)5g\M+e:~'X4abs^[a* G!qȠɊ×~B僬}$Q~ԩ빗u˱أx%ʭj;8)E`eMFOOSQ=``D4ʀVҘOh [&Ѧ`_Lr [wx `)MHR!(Gib6=CDO( f c>Fq1'ZbM7PDt1k18ܭS"q6 ~E~\WHxX_7jPׇ˝usWfTlK4Asul+Nf8!YZyl 2܋`a MoBGyZm\ˊ#ƴKgĤwzlL;᭯Ecp~aHTes&;Ӕu \7*k[*5rjX iJ@70Ob!n-M >ēAZ˲Giܒb6$2τ8M;} 1k0ݪmcl zW%wR@me%RH`T!oeO #YVbfգ4W'p: b|g`25BOޔZ ٥QBf2_=hIsv ]ͫ{IeC7Y&1Bwot*M5WdQ ƯkxR;YL_bz"1dYwN>3Zn@ 9|[7,̡X@HdJag.|<ޜERt8TfNۀCO=![nQd>)56WQm^Nl/$2XSWlиD"t,# +c* ޽BIAm8 gsGrL+n h^ڡ^kwrcr0@plG|HsHk:Ho8W pB;ۦKzeL$5nf^`GY$IKz4ԕdS; ?c)60l_BGEyӑ0 =F[V{de*XMNi?Пɗ &*ώbJBď;n9P Rv0 ]4# $#0qiC4QTҝq~B'|'Ϋ΀;i<β%u (=:/8?w6?{= nzO#g3׺*&Uum=k&?2@d qnL[r#UL)Txe5$[U +dl;'@~B ;OLy$<]hnStwz#l:g4#jhx 62 19ۑzl,MO: 6"XPYwԖ>&X9rsAv굸)#]#b5k.ZT)sip<)98aHf "C݉0.8Ov|p+ ,LglyGtkkWAwyȍ8lSPST/XL>22ME/$ ;kYY0Bs<|Yg4=)'NrBgcy+CkqZjNhDfi 6&yB~t ءq1"d ܖdHi"@,w\? eL>ۻ鏉#6&cDh1wۄNq];8'*DM }gaL,B[7]]0 byau${:5( ,-pMʹeEG%zfsU7Ut4|VKs7ӫ:r=}Wfw a-@D 5T;@ۜ-Qq1 U5w[ge('ȧFO>U G4'(_@q_=2t!FzHDŽMͲSQVMn*{CpEۿmX: ;e{Nz펻6砫=pp 5#'vy.bi'>ڵzh猥8Mʠo}N9.h$$x@v)0{eΙ&7.9& ?Ϥ4=`%#~=(V^U)*=/NEo XI h6V?J9ĬŇ&+434_T+ܝ:jd%Uu"F6NUs7DbǞ xX̌Gx +/S!|,lDZ[Mc Zm sĖ|A>8=14 hośV5FY%5lmX{Td/IѮn(.=s ;tL3o%yRlFL9F 9EU0͇^Qi:blME0¨=VJ9\`ڌ=z?;|,#$=7iYl/XpD0mk9 " b/ +{. z555R+V:d󆛯 &14Metf*`7Ȱf ႛvt4S taJU&rD6PXQ8`ʦ7$MD)68@q?O 2w,gC8# S׫?hpl;Ti'H@ٔPf{=&QY>#rUmjҐf¼@w4ۼTy7r$VIN띿xأ cB%sus PQ-UYZ3^1#,< ;}%>Q$\V.+ ̎)ԷYQZ̀w8úH.< xt=kf@,+ݰzvp0w[ NCG\:hٺ0|QDi3a &Q2sL>`}|EG1Ǔ(K x1t14 4݆z.*$sB;;-@\cWEyQ Ovd3>:|yzl$W7n^4*<]񝸦Yg$5w[gG1 P{{M_,eD>b'Х1WdB*Lwui+F?\*Aɉ \ V'ڝ(uJ,A\#H'[k[a^J [9 Z{@oÁ\tUӃX_?]kD}VH,$Si2v$>2OFf#3t@,?n٘$5X1x][:ΨtJ!]-E'q= Ƞ2h4\II :˜)y8x͘Tig#5]IF$DSR. v &:a EIv:7iH%( ! .ԁ4hN2[aH;RȽ:iλ1!cP.$*E(̺t][jL $ c ҷiAN>NHP6dAK6KC%%}%=]R Riw}i:QcKSCp$[~8d8 OGgeA/ e^nqgE^2 J-n\j'Dw ? W0e$Kl\&|^!naphV10C&/ޒcdQuӇap.$j '=uov˂鞦|{#*0 6wbP@ւ:P S0}=gEH*ueGg P3l:"vǭ]k}MvSoAb^--7_i[jm7m>/93>] I` Ĭ>sP$ FVĈ! C' q L}Dg!RKUCkЎy7F3rZ1#_G$# xp?ps|tkάKuC?x2w4,#l uW KHCX"zd=pUȊ81a|BI8CC% k~:3fshϺ΅ul8C xScl\%1EVxCӻz<>9k- xPrOjdaI^Hq9cڠbbL0tʡbn#XXԂCuH?Sr)\p.ݽ4{۝ A\d :ޏNSjq xq@tX򌊚7[({x&b'vײoX 6{h"ފyLq ]|uzax`#Fp*){|l{$X.U{9AN}aVt$ɡC ^Op}I{nU ?c4o8}R"FvY씢ޯRKYq9)-Wp? L,Q3 H`8@~40cĠ8 k291Fmw1n Ȼc2; &BKCE2IgY QXj6<yt 4I;Iǻ 5yzБ{{s/ lcFscqip;u26wy Bv`CI"%N4{CQ45F ^c[;kxѱ*1v *-v!Kl^˼]WQ}{l+J&UOU1;pF{S, $at٧a8N}Ap{pl6WލMo6kiH \@) 'y͘HV]湳m6mۮm(t`]P:3nf]Тh?4ɮYK;t@ڬ-Xy31/{L ~GӇ HQy2$I߉FAV'Bر1o:c}+&fEw;ӥB&u[ TDfh|w0+` *phԬ6H ܸtBv _va.hyE\M\7b]o0`?tϻ#>gk7P.^^IrT(1.P@G(21@6U'@jc[`0칚qU[\gpYl]nwȴ)(YSW,m-3]YPEy`i4¤rSi\dfQG2މ̾eP rW{0vR' S#c\|7ĄG0 8~ Ȼ;3tSlLL0,y&Dn221m_89D?'(|:̧vjr%y2!\̐#GA懭|Ƃa=8ʈi<1TA݅I;UTZlض 6uJ.ل?@SDfVHPDCZ8". ?pɨ˿t&IE&vUMQ1`4! 04s}WM1 ԋw"Be:(HfyF9J. m\$M&=ӯ'S Z?$huu!\]صtWĆtin} `{쀸-}JH]i0Ыp0E5RtF{jk 侽sv3C,XZw+^I 9Wz 'ޮIGp{*)̷K -pQaP3b#2kSL=\Q%x2 #kX;8:tΦ}1}Ȯ-؍FRGoijqe6WI8 D+NOi0.CvPQWlx| 8c̶bWkG+]oc.lEџ{ !Þns<!/l闀wQžI1uCElrȊl,UbLT|t36 J&P߯OH|J0ygg Sj 4u 2`a\!em F1dgݜܻ;,/%f4%^4!kە(F+amw2<c:s=.4FH*2h3ƥs"g:ztmvW)ezҹyYD(ڠ͓nI\sT1bx?c[qr>$Zbt"Sݔo.ΣI2ϭ?eY_H( =<}Nާl$m"L-zB-Sq<taNc{EMQx)êʢfCh>ۀۉf$Mr\7ZzEI5H"ql>?:-Hp\tFwłq5${B/)<3A&LK6b!0qgQƷ{ԃǵtGSp1 `xb8lV`pFH"MR4R UyG"pXAG BiǎXGC;@'~MwK\ܓQ1Pki뙥*D5yӔ`M)V 9ƕÍ]AdRoFي. +ԁ\Rf9B WzM7 I8uÑ S2zK0U’"2{iU~9W;#Cv}3r|4{ >vh3ǿ=f `3D c/G|r9X -6hf+؁ a|")jXǚFb = :K_ J5b!3dhBLΝ)&iWP-b⤘L UX MN.V4ÌqOGds1O6v-3] jR|X J;'ƁgF+^94(D7lt}SF, >wg]Hv.&?M OlC?Cu\ 1Y?u":eis@[4c7:`:N)_{Y{rsiv 93; qcwX;]4Uq՚ *3 )1l8k瑣$H&Nz1;(~JG~1=(:82ӂka Oכ"pTL8s<n%Ykدo8!éf/c>$ Y ^LD.\Kݧ2 @:3jcž%`ւ67\#)[#-޳'Xs=?NzsjYt:w6ZQ[K,uJPdu|-Iz٢߆oWzg] J`[ܜ'pq㻤$ACV5qM 2?2ߡRtPmVi"sFMFRʑG 8{?mv@(w%@ tgeR 5..BP>ɩ=_` ^&V=Y{dp9qHH ܀A0LH{ӄp;Yɝ.1izkQڅ>:I*ݪ{TTj"`fhVxh0&[9Z)mqwckgrpao2m{ґ'+d("sʀmcAxE\ˏN'˟=tqEćfs0g]Mv~af hʚ1FNwd =],Z6RQ = 얪D/]TK,3OSS:syDx76pyU|+cF%C FRnWd%+<;SZW,QX.R7+hߧݭXv|[%k:qE10JӼQCOlcu7>gxz^юˠӫќw{Cc(:Ū|F;2Ⱦs@גoM$ˆ鴚  uM64:g>3Cq8ޠJbO>l'-] ❟La=|kg]J (>(W}N8o`jZdN,Lis I qyc9R5l?RמHѱ4$PTO$opͫ/zEä(>>indq)Ƣ'%OtR54ø;];(nrvg&E"NwSgz9tizE~Ì"n"f9ϼM5 m ʀv<{;&{iɩ wᘡ1C{6Wb*L* x>vAUv'#6Qw٪Sm;169fT=KUuwY\;@Avg,J-]ul'꧵[o“Bҽc <ȸa529|EѨY$=\ƬJo:NKp1C, 1> FnY~brw(: B$xBT@Y$ tjZݞ:I6ݺIn+Y-@So<9M!y!r U?O$ڠҏ1۞  RGw  52 IidN ^J(&qqq !yJ*ʦn2M#|f(?"^f"tSR춥LQQE]+IG+{_GQM$xFz^"3t%G;MvWO;AD@%Vtlw+MKNe%r#K0d^TSvu}&=ʹfFH$o陗j/OggJi (]6;j@"s>}d! ueJ)Jt>\=b_,-bkTy'tRBG'=wܱ-dKEA9`B4IrNL~|p*9IЕ>bsnS¼U ض8Q疺K-a< ?=py<^GㄊP6P JLA^I3p669J'O.E okrRa(>;T:7#HFp-RsW"#P'(tFA}X3nO$i1:oՌs@3'Z]"s]B?2ip -!.1&z֧/ -E[,"y4b9H<*l(OwS>szO~<@랒Oa} (!D\"lםFҤaþ*<.?RL6 N4u'oBe0!s/E!$L_ϓ6Z%vMA%hHPq,X2mF'1%206 my&Nf?_yzI!UbEl7txRi"OC xs's"y8QNSFI>h R CvAveeY|0r%mxƜySEgxrEWAi~Kd 'nJ%?@$Nqα.{P>Bl][$>ao\. wRhjrsk< `D7q&1[4>KlEayt|.35CYK~ʞ_إB\UP\9DrwÜD C'7qJj&4B)NqA=%'r0" =ovq'{z0, =GGE+9D;* lAC tBR]#*ҿm.n9`pQnBtO~edxhTVY1ϟףo@![ , F{K'(`˰˜VpS'OpEnfsEScS5{pJ L'iH [_\%1ȄUF6n˵Y$ce:%}/(&Irw#x{هB1Uz5(T F>#Ɗ>LBus A fq EzƁ2{q\#ܙ>0s#^\sW~}ѭhm~钧X"Ů4tw.yOyI'm{vJ%?uƵz@TL'Yƍ#{8̣Mc7ը:{vfF+Yx6x2 %9'ֈVyxk#ys#0٠8Y9v0Xk&Aݑc pKhgm\\A-p0Ǜ"yY Nj݆Y:wNgW$TO^tj ψxxXfAaeX IV]i4G8þCC`x.GX |/!B3kY܋2GPT]Bb޷=AE6]3Nod%_H9t8DN!ʠlİǵnL>AAEh1|Pw(Aqg3]&[MiRDـF]cT|tq=$(l+ Na項8A\T lNB)ӊ%8'5([q>5$ B:}H :/pՃM#g%[X_0􏁣9A fW8ChH RWtmdW/Ǝ ¦ Q1>O6-\\+t*"FG3F@^i"n$剩?d&u2;Ni\՘ 'wL~Xgh>+j&vJDfLwjU^3|ßV5gm_&+4ZmX%-<HWa; py2*:wI,Cȁ&[26;ԇۏ3䮂4&[46g`FH*r?zRuMvn"af{bMG0P9,[@q2x7/v' L]"Jx\/yPG!cVY]P/)iH+a {7ehG}D<+m3~<.\3ODD*sbXegYLWEs^="k󅜱S |XzMC^ !e2_(&Jב  ~ijQIg'z6ظYγXWmzaP&&a6$h&S25}k66'Z N=OrZLPQ'98/=ˌ;Fa;goxob8X"F蒑q@N,h,ЛqAOt|ٺ-"p(3JmeRCYw|eh%t\ K6%Jf678IoD:ﮚq4KρpJx#w%P7 innf|6EsF 'tLp*0lItw6d.>Bȷn çGzl*I5eC`/[ʪ$KČ)J!- qU",z$u# 6ZgAa6HٻTc J$5-P>, EaB'ޯs7T0d%Q>P>Ye?ԸD3쉃}vNDh0p@oBu}͓fHR#R>ZUt]BO7F"Q(ZmC)㥘ę$^Irb 1_ɧ1/R9z"KR.$pߕ} L,658:O v't|Bއ&f $Myyd=a f^N_i鋧557u&07$&1CbTUtRUҺwFf6NҼsr9hr:bDNt Fvbh.ӥH.)۵iE|\*μ}WC2ZI>əVaTӬ$K6Z䧰(3}S88pע)}DN8# arX -&& ޔjxH⌂RS4L9wҲ:78qi4M)GcL.Ƭ$^`ܶ]8;2:|ãW >avi"8t5N T\>vL dG|&c<$,dkeʺw+cw+g`Hv" =#-Q6Awk#8cbPd,41``^N_9YsX3= BI F;Ai6h`2jLqڨF)vG-L);[sZ*Eׇp'l]\t=JHG.L gu($o?}`;wR)gcyI&AM4adBI8 +҄=^k7-sh();Cs۪Y Rn*Wnv}F#\>'MV.j vg!*jX=6F  H%px߃\"sGi zQ.!ty|t"^i~ w l׆0CBBUx|٠C , ԡ]@qT2 #c['CS^ȶTik/^g(ڃ*tw%A )Qp˩=Bձe@(mZc8$1"F$qyޥin#,'i-ˇjނ H9LLކ8:uC&30{|o&4qfo4 TY5ԙ7neҍGGMe$U:_;6aZP4"zNjTsDa=M#dgUL qq;}{E'Q(Ա=$~OLInѡ~.y}/# X\wTL nHI ֠>bJQ9ɁVCQz 4@.,;nCcdd)<8pZ rgf3c!Y慳gAZE,7:l.pdO hY&ٝ5LS`Q,dD@NtuQWSLm68Cz[bgxW00V ]d:"V]]*&wyQ9KS6+ԩq-Ǿa]<!4 3tDu`dž} 5v/zd=qWsUsl}M ĤÈ)J 9}{ Љ2CY+uF=`C"ZKѧscXNE15/ \wy9i@N%`'W5 }>R֖ ,~լcPtwyJxn[Ćr"ؠ F03&<\gRQp ί+t<\ l0C o i*Bbq52f j$ ĝzs`clDGnϧ_GV˸3{tt'uHBҐ:W|u9{STq܉3BAfϨ Hx'+idĚb:8Mf:e>żl8Ehe Fg뷠^inUnlr~u|ܟ :'&lýރb@.5ŻUpQ^\0#MP 0ռA.J%H>0jQ@ F}䭆_ ;=5ZeT{sӔh# $yܔ|z+I|)<6Ί{v͇T8A(̓uUǩ?pKCΎ}Z&'~rYUO5>:lՌ1%f!g*ƒ}SJ1[a朓\%t=#xco0Hj%jXBw|QoBO'r@bQc2kFaE!/<2i/ѕ{&Tl. 6] ĪA3$?Y &c0; wS0`0 ZBwt 5@T~sLFC>2Z7'wۂKڂ(|,){Q Aw B4akZT&ԤNg \/kTzOU%U?g*Q\` +yC¶(Lp:$~MRP9L8vCT5ϑֆ;(lEe\huzSsb-sg&2ŮN'+n6Lbu0 rLj_yqxpx]缳JB[ H32y7FD<(10a8!ZE\,֌v͂:܍ A)&!/t* ƘrX#SCF*%xTbq [A/%qhx%;WŔVMOu5 фSӉi&YRK. txC\mVIțWYoj;n|:j<V=n6-G 属23jIÙ3#7W>zl0'=ZJD<-&elqjL9?:#MzA|s6MٸxKz[y"!PFq܈hy#?[<O1{lΚ `3vͭwebg,s mUkMnqX6Z[`KqnYTaٻa?E4aeպ5 8riܪ5˵V/]LBKgF{}1doL^UUtSկl6b1c EaRjOĽfe o;*C㎜_6B9aةLM)d uDqsc6Z)!eqj$+݄WxNۮKMv9RLmlAa[Fot|l3=% .mwC$Yؼ*VF\/F=brOܪ@m{vF;hO{sl#h~$:UiXTBצPC%⛷ҐBztPLΙ >+';ݻ>:j Hkþ+m:bEN P$$ !Z߉݈&ws:/wy6Zjz!6gH_]:gJ0BԝRd%juRh{'ٜ ]q- &q]H!`A\uOaGboAM UH]c`Bۛ~3yh4f%f%YI@W43v4"^1%4sLYo^Iˎf۸ۑ!f2gF6"3Auh~*>i绝ok]oV|47) >f]G*զowa{uWM٠)pٔ =ɫ.|lʢrA/\s.w o;On^ y$%BG^2u\ [DW2k` hÅɜs&7Ttc27t9ɇ\G~CGF6y-nOv5%0y!H/kOSϩ7RzΥy1g)3yLL֞<Ū;"E8Yi/TvGŅhK*j YJG%MlۚhP}`6[uOa/IUrսZP,Iїwzz݆k+lY'RPUNtS2T"+5=E-%xJSqӬVmj2 hK^n9&e.k'f,C@f:{M X~xN84؉fҿ %msogP/͆ s=,8IBt_`XRpkΆi<덺9VcTwÉ :]X_|< >CAN]&Oׄp4I[Xc~9C.[-P"cǃBo {1Q@|BD=ȰSOwa1OW%"}4 ZDy"ƕqom0 /^:DYǰ=k9 5EmxPj/t611M^{~1wjrFX{ɹqe7BH![$+ilWAf!qc*(iG8 ;ʩ=f5A aΝV?V*2ZKZ}߼vÛWo_ NMTm:\%bG,EjlŗC 4Im#_c2A lØ s0k?`t#!.ݸ`ڥ;\YҮYmvTo$q^*(YtkY/ Q[j"MSN" uczu SL]zﭡO~OMkkĉԤư:.&z*ܼyo>hXoo5Yݿ_~=Qb;aomU܃mqD8oN7r018Rj]x-Bah{*)>V{wPuLC(b{ Z8a׹p7R903@|$ؖT$Z}FP:rpзI_D6Coմ<#zFVJ0Tq5x=:z(ݖ"Oȥl.juSPp"~EܽV`OFצ׶=ѝ̾h%kg0M ==Zlv- Kubernetes – Capten https://capten.ai Thu, 15 May 2025 11:57:09 +0000 en-US hourly 1 https://wordpress.org/?v=7.0.3 https://capten.ai/wp-content/uploads/2024/11/Capten-Outline-Logo-Icon-100x100.png Kubernetes – Capten https://capten.ai 32 32 Containers Containers Containers https://capten.ai/blog/containers-containers-containers/ https://capten.ai/blog/containers-containers-containers/#respond Tue, 06 May 2025 17:36:14 +0000 https://capten.ai/?p=25173

Containers seem to be a buzzword in the industry nowadays. As more and more companies are undergoing cloud transformation, they are hoping to convert their legacy applications into containerized ones. This is being done all while trying to adapt to a microservice architecture, in lieu of a monolith one. But what exactly is a container? What are its use cases, and what benefits does it provide? Let’s take a dive into these questions, to see why companies are so forward about adopting containerized applications.

What Are Containers?

Containers are a type of virtualization technology that allow you to run portable software on any machine. In essence, a container will have the code for a software product, and all of it dependencies and requirements, such that the code can run anywhere the container can. This means that containerized code can run on numerous different machines, even after it has only been developed using a specific environment.

What Is A Container image?

A container image is the package of the application code, its dependencies, and more. Images are usually executable, and they contain everything required to run the application code. This will include stuff like system tools, libraries, settings, and even the runtime. The instructions for how to build the image are usually within a document called the DOCKERFILE. We can modify/create a custom dockerfile, in order to install our own dependencies into the container image.

What Is The Difference Between An Image And A Container?

An image is the first step in the process of creating a container. During runtime, container images become containers.

What Problem Do Containers Solve?

Normally, when you want to run your application on a server (i.e. a host), you would have to customize the entire server to be able to serve the needs of your software product. This can get very expensive very quickly.
In order to take this a step further, we can create multiple virtual machines (VMs) on a single server. This VM is a type of virtualization technology that allows you to run one operating system on another (linux on windows, for example). This way, we don’t need to customize each server, but rather each VM to accommodate the software product. This allows for less overhead. Each VM, however, still creates their own operating systems.
What if your software doesn’t need access to the entire OS, and the OS just serves as extra baggage?
Containers aim to solve this by taking the VM concept to the next step. Suppose we take VMs and reduce their overhead. This can be accomplished by each container sharing the host’s operating system. This way, we can get rid of the extra baggage. If a container needs some extra components for your software to run, then we can install those within the container itself, instead of on the host. Their small size also allows you to put numerous containers on a single host.

Containers VS Virtual Machines

Containers can accomplish the same things as VMs, all while:

  • Reducing overhead
  • Reducing disk size taken
  • Reducing startup time (seconds instead of minutes)
  • And Using fewer resources

Container Orchestration kubernetes

Since each host can hold multiple containers, and organizations usually have numerous hosts, we need a way to manage containers, a process known as orchestration. For example, if you need to allocate 5 containers, then you would allocate 5 hosts to hold each container. This is to ensure that if a host fails, your containers are safe, and your software product does not experience downtime. In this case, it is best to use a container orchestration tool.
Container orchestration can be used to build, manage, and automate the management of containers and container infrastructure.
This is where tools like Kubernetes comes into play. These tools help you automate containers to the point of being able to offer zero-downtime deployment (apps can run even while being upgraded). Self-healing apps, and automated scaling of resources. Sometimes, cloud providers include container management tools out of the box, such as Google Cloud offering the Google Kube engine. Docker swarm (offered by Docker), Nomad, and Marathon are both good alternatives to Kubernetes.

Benefits Of Containers

Building upon the benefits that containers offer over VMs. They also offer:

  • Software portability – your app can run on any machine.
  • Isolation – software can be split into separate, individual pieces.
  • Scaling – can increase/decrease resources as needed, saving money.
  • Automation – saves time and money for your organization.

Limitations Of Containers

Containers do have their own limitations, even when compared to VMs. These are:

  • Less flexibility than VMs – currently, you can create a Windows VM on a Linux machine. However, you cannot create a Windows container on a Linux machine, yet.
  • Orchestration challenges – due to their large numbers, managing containers can be a hassle. This is made easier through container management software such as Kubernetes.

So, What Is Docker? 

Docker is, officially, a container runtime. It is a piece of software that allows you to build/create/run containers and their images.

There are other options besides Docker, rkt (project has ended), and containerd.

Use Cases Of Containers

  • Microservices – normally, apps are written using a monolithic architecture, where each component of the app is typically part of one service (think of this as one process doing everything the app needs to have done).
    If that same app is written using a microservice architecture, each component of the app is written using a separate service (with its own endpoint). This allows for each service to be built/modified independently of other services.
    We can then deploy these microservices each within its own container to allow for easy and efficient scaling, whichever service is being used more often, will be scaled up.
  • Cloud transformation – this is the process of bringing an organization’s existing IT infrastructure and codebase to the cloud. This can be accomplished easily with containers.
  • Automated scaling – container orchestration tools can allow for increased stability and decreased running costs of the app you have deployed. This is achieved through automated scaling. More copies of the app are created when load is high, and less are created when load is low.
  • Continuous deployment – containers allow for the easy deployment of new code automatically and frequently. This increases the stability of your app through easy/quick bug fixes.
  • Self-healing apps – these can detect when problems have occurred, and take steps necessary to rectify. For example, suppose a container has run into a problem, the server can be set to automatically reboot. OR, since containers start up so quickly, you can just replace the bad container with a brand new, working container on a completely separate host. This will reduce the startup time immensely compared to the first method. It will also allow that host to be restarted, all while still maintaining access to the container (now on the new host).
  • Developer visibility – there typically are problems between development and production teams. These are due to an app working in development, but not while in production. With containers, however, the development team can use the same container image the production team is using. This completely gets rid of this issue.

Containers In/Versus The Cloud?

A lot of times, there seems to be confusion about the relationship between the Cloud and containers. Let’s provide some disambiguation:
Cloud providers allow their customers access to their servers. These same servers can be thought of as hosts for containers to run on. Therefore, you can run your containers in the cloud. Cloud provider-offered containers are super cheap, include extra tools for orchestration, and have good support right out of the box.

Conclusion

In this blog, we learned about what containers are, their benefits, how they are built, and their use cases. Using the basic concepts you have learned here, you can try to containerize your first app using Docker’s tutorial, as the next step in your journey with containers.

]]>
https://capten.ai/blog/containers-containers-containers/feed/ 0
K8s Hello World https://capten.ai/blog/k8s-hello-world/ https://capten.ai/blog/k8s-hello-world/#respond Tue, 06 May 2025 16:59:19 +0000 https://capten.ai/?p=25166

Imagine embarking on a journey across uncharted seas—the world of software deployment. Navigating these waters is akin to steering a ship, facing challenges that range from smooth sailing to avoiding treacherous downtime, all while adapting to ever-changing conditions.

Just as a seasoned captain guides a ship through unpredictable waters, Kubernetes emerges as the expert navigator of the digital realm. Think of it as an automated navigation system, expertly guiding your application through the complexities, ensuring a steady course regardless of the turbulence.

Kubernetes, often referred to as K8s, is more than just a tool—it’s an open-source container orchestration platform. It simplifies the intricate tasks of deploying, scaling, and managing applications packaged in containers. Containers are like compact, self-sufficient units containing everything an application needs, promoting consistency across diverse environments.

Through this blog, we’ll uncover Kubernetes’ key role in orchestrating deployments. We’ll explore from setting the stage (environment) to crafting your app’s performance (building container image) and culminating in the main event (deploying your app).

Prerequisites

Before embarking on the process of deploying your first application on Kubernetes, make sure you have the following tools and accounts ready:

  1. Docker: Install Docker to create container images for your application. Refer to the official Docker documentation for installation instructions.
  2. Image Registry Account: Sign up for an account on GitHub , DockerHub , or any other container image registry. You’ll use this account to store and manage your container images.

With these tools and accounts in place, you’re equipped to begin your journey into Kubernetes deployment. Let’s begin!

Prepare the application

Clone the Repository

In this guide, we’re using hello-Kubernetes simple web-based application written in Go. You can find the source code here .

git clone https://github.com/pratikjagrut/hello-kubernetes.git
cd hello-kubernetes

Understanding the Code

package main

import (
 "fmt"
 "log"
 "net/http"
 "os"
)

func handler(w http.ResponseWriter, r *http.Request) {
 log.Printf("Received request from %s", r.RemoteAddr)
 fmt.Fprintf(w, "Hello, Kubernetes!")
}

func main() {
 port := os.Getenv("PORT")
 if port == "" {
  port = "8080"
 }

 http.HandleFunc("/", handler)

 go func() {
  log.Printf("Server listening on port %s...", port)
  err := http.ListenAndServe(":"+port, nil)
  if err != nil {
   log.Fatal("Failed to start the server")
  }
 }()

 log.Printf("Click on http://localhost:%s", port)

 done := make(chan bool)
 <-done
}

In this code snippet, the main function sets up an HTTP server to handle requests. The handler function responds to requests with a “Hello, Kubernetes!” message and logs request details. By launching the server in a separate goroutine, the program continues executing, allowing you to interact with the server via http://localhost:8080. A channel is used to keep the main function from exiting immediately. Understanding this code gives you insight into how the application handles requests and concurrently manages server operations.

Understanding the Dockerfile

The repository also includes a Dockerfile that employs a multi-stage build process to craft a streamlined container image for a GoLang application.

FROM cgr.dev/chainguard/go:latest as builder

# Set the working directory inside the container
WORKDIR /app

COPY . .

# Download dependencies
RUN go mod download

# Build the Go application
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# Create a minimal final image
FROM scratch

# Copy the compiled application binary from the builder image
COPY --from=builder /app/main /app/main

# Expose port 8080 to the outside world
EXPOSE 8080

# Command to run the executable
CMD ["/app/main"]

Let’s deconstruct each segment of the Dockerfile to grasp its purpose:

FROM cgr.dev/chainguard/go:latest as builder

In this line, we’re leveraging the Go images provided by Chainguard, based on Wolfi. These images are tailored for constructing Go workloads and follow a “Distroless” approach. Distroless images encapsulate your application and its runtime dependencies exclusively, omitting package managers and extraneous components found in typical Linux distributions. This practice, endorsed by tech giants like Google, refines the signal-to-noise ratio of security scanners and streamlines establishing provenance.

Distroless images exhibit remarkable compactness. The smallest one, gcr.io/distroless/static-debian11 , weighs in at around 2 MiB—roughly half the size of Alpine (~5 MiB) and less than 2% of the heft of Debian (124 MiB). Chainguard offers both a minimal runtime image for executing Go workloads and a development image that encompasses a shell and standard Go build tooling.

# Set the working directory inside the container
WORKDIR /app
COPY . .

In this portion, we establish the working directory as /app within the container. Subsequently, the COPY . . command duplicates all files from the host directory (where the Dockerfile resides) into the /app directory of the container.

# Download dependencies
RUN go mod download

# Build the Go application
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

This sequence initiates by fetching the Go module dependencies outlined in the go.mod file. Following that, it proceeds to build the Go application, meticulously configuring the compilation process.

# Create a minimal final image
FROM scratch

# Copy the compiled application binary from the builder image
COPY --from=builder /app/main /app/main

This section introduces a fresh base image called scratch. This image serves as a blank canvas upon which to construct. Scratch finds its utility in crafting base images (like debian and busybox) or extremely minimal images (housing only one binary and its prerequisites, such as “hello-world”).

Subsequently, the COPY directive transports the compiled application binary (main) from the builder stage (the part marked by FROM cgr.dev/chainguard/go:latest as builder) to the /app directory in the ultimate image.

# Expose port 8080 to the outside world
EXPOSE 8080

The EXPOSE command signifies that the container’s enclosed application listens on port 8080. Yet, it doesn’t publish this port to the host—it necessitates specification during container execution.

# Command to run the executable
CMD ["/app/main"]

The final line defines the default command executed when the container commences. It launches the main executable—the Go application built earlier.

Building the Container Image

  1. Open the terminal and navigate to the repository directory.
  2. Build the container image using the following command:
docker build -t ghcr.io/pratikjagrut/hello-kubernetes .

This command builds the container image using the Dockerfile from current directory. The -t flag specifies the image name.

Running the Container

  1. Once the image is built, run a Docker container from the image:
➜ docker run -p 8080:8080 ghcr.io/pratikjagrut/hello-kubernetes
2023/08/08 13:25:24 Click on the link http://localhost:8080
2023/08/08 13:25:24 Server listening on port 8080...

2. This command maps port 8080 of your host machine to port 8080 in the container.

3. Open a web browser and navigate to http://localhost:8080 . You should see the Hello, Kubernetes! message.

Pushing the Docker Container Registry

For our blog, we’ve opted for the GitHub container registry. However, feel free to select a registry that aligns with your preferences.

  1. Log in to Docker using the GitHub Container Registry:
docker login ghcr.io

2. When you run the command, it will ask for your username and password. Enter these credentials to log into your container registry.

3. Push the tagged image to the GitHub Container Registry:

docker push ghcr.io/pratikjagrut/hello-kubernetes

4. Verify that the image is in your GitHub Container Registry by visiting the Packages section of your GitHub repository.

With our application now prepared and containerized, the subsequent phase involves provisioning a Kubernetes cluster and orchestrating the deployment of this containerized application onto it.

Setup Kubernetes cluster

In this section, we’ll walk you through setting up a Kubernetes cluster to begin your deployment journey. We’ll use KIND (Kubernetes in Docker) as our chosen tool, which provides an easy way to create a local Kubernetes cluster. However, we’ll also mention alternative options for local and cloud-based clusters, ensuring you find the setup that suits you best.

Installing KIND and Kubectl

Before we dive into setting up the Kubernetes cluster, you’ll need to install both KIND and kubectl on your machine.

  • KIND (Kubernetes in Docker): KIND allows you to run Kubernetes clusters as Docker containers, making it perfect for local development. Follow the official KIND installation guide to install it on your system.
  • kubectl: This command-line tool is essential for interacting with your Kubernetes cluster. Follow the Kubernetes documentation to install kubectl on your machine.

Creating Your KIND Cluster

Once KIND and Kubectl are set up, let’s create your local Kubernetes cluster:

  1. Open your terminal.
  2. Run the following command to create a basic KIND cluster:
kind create cluster

3. Check if the cluster is properly up and running using kubectl get ns

It should get all the namespaces present in the cluster.

➜ kubectl get ns
NAME                 STATUS   AGE
default              Active   3m13s
kube-node-lease      Active   3m14s
kube-public          Active   3m14s
kube-system          Active   3m14s
local-path-storage   Active   3m9s
Alternative Setup Options
  • Minikube: If you prefer another local option, Minikube provides a hassle-free way to run a single-node Kubernetes cluster on your local machine.
  • Docker Desktop: For macOS and Windows users, Docker Desktop offers a simple way to set up a Kubernetes cluster.
  • Rancher DesktopRancher Desktop is another choice for a local development cluster that integrates with Kubernetes, Docker, and other tools.
  • Cloud Clusters: If you’d instead work in a cloud environment, consider platforms like Google Kubernetes Engine (GKE) or Amazon EKS for managed Kubernetes clusters.

With your Kubernetes cluster up and running, you’re ready to sail ahead with deploying your first application.

Deploy application on Kubernetes

Now, we’ll deploy our application onto the Kubernetes cluster.

Create a Kubernetes Deployment

Deployment in Kubernetes serves as a manager for your application’s components, known as Pods. Think of it like a supervisor ensuring that the right number of Pods are running and matching your desired configuration.

In more technical terms, a Deployment lets you define how many Pods you want and how they should be set up. If a Pod fails or needs an update, the Deployment Controller steps in to replace it. This ensures that your application remains available and runs smoothly.

To put it simply, a Deployment takes care of keeping our application consistent and reliable, even when Pods face issues. It’s a fundamental tool for maintaining the health of your application in a Kubernetes cluster.

Here’s how we can create a Deployment for our application:

Create a YAML file named hello-k8s-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-k8s-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-k8s
  template:
    metadata:
      labels:
        app: hello-k8s
    spec:
      containers:
        - name: hello-k8s-container
          image: ghcr.io/pratikjagrut/hello-kubernetes
          ports:
            - containerPort: 8080

This YAML defines a Deployment named hello-k8s-deployment that runs two replicas of our application.

Apply the Deployment to your Kubernetes cluster:

kubectl apply -f hello-k8s-deployment.yaml

Now, if you’re using a GitHub registry just like me then you’ll see an error(ImagePullBackOff or ErrImagePull) in deploying your application. By default the images on the GitHub container registry are private.

When you describe the pods you’ll see warning messages in the events section such as Failed to pull image "ghcr.io/pratikjagrut/hello-kubernetes ".

➜ kubectl describe pods hello-k8s-deployment-54889c9777-549rn
...
Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  2m40s                default-scheduler  Successfully assigned default/hello-k8s-deployment-54889c9777-549rn to kind-control-plane
  Normal   Pulling    75s (x4 over 2m39s)  kubelet            Pulling image "ghcr.io/pratikjagrut/hello-kubernetes"
  Warning  Failed     74s (x4 over 2m39s)  kubelet            Failed to pull image "ghcr.io/pratikjagrut/hello-kubernetes": rpc error: code = Unknown desc = failed to pull and unpack image "ghcr.io/pratikjagrut/hello-kubernetes:latest": failed to resolve reference "ghcr.io/pratikjagrut/hello-kubernetes:latest": failed to authorize: failed to fetch anonymous token: unexpected status: 401 Unauthorized
  Warning  Failed     74s (x4 over 2m39s)  kubelet            Error: ErrImagePull
  Warning  Failed     50s (x6 over 2m39s)  kubelet            Error: ImagePullBackOff
  Normal   BackOff    36s (x7 over 2m39s)  kubelet            Back-off pulling image "ghcr.io/pratikjagrut/hello-kubernetes"

This happened because Kubernetes is trying to pull the private image and it does not have permission to do so.

When a container image is hosted in a private registry, we need to provide Kubernetes with credentials to pull the image. Create an Image Pull Secret to store these credentials:

Create a Docker registry secret:

kubectl create secret docker-registry my-registry-secret \
  --docker-username=<your-username> \
  --docker-password=<your-password> \
  --docker-server=<your-registry-server>

Attach the secret to your Deployment:

spec:
  template:
    spec:
      imagePullSecrets:
        - name: my-registry-secret

Apply the changes to the Deployment:

kubectl apply -f hello-k8s-deployment.yaml

After applying the updated deployment you can see that all the pods are running.

➜ kubectl get pods
NAME                                    READY   STATUS    RESTARTS   AGE
hello-k8s-deployment-669788ccd6-4dbb6   1/1     Running   0          22s
hello-k8s-deployment-669788ccd6-k5gfg   1/1     Running   0          37s

Access Your Application

With the Deployment in place, we can access our application externally. Since we’re using KIND, we can use port-forwarding to access the application:

Find the name of one of the deployed Pods:

kubectl get pods -l app=hello-k8s

Forward local port 8080 to the Pod:

kubectl port-forward <pod-name> 8080:8080

Now, if you open a web browser and navigate to http://localhost:8080 or use curl http://localhost:8080 you should see “Hello, Kubernetes!” displayed, indicating your application is running successfully.

➜ curl http://localhost:8080
Hello, Kubernetes!%

NOTE: Port forwarding isn’t the optimal method for accessing applications within a production cluster. In such scenarios, it’s recommended to establish a Kubernetes service and employ Ingress for handling traffic.

Conclusion

To wrap up our beginner’s guide, we’ve navigated through the steps of deploying your very first application on Kubernetes. However, this journey is only the initial leg of a much larger expedition. In Kubernetes, a world of opportunities awaits, allowing you to optimize and fine-tune your application’s performance, scalability, and resilience. From advanced networking and load balancing to automated scaling and self-healing, Kubernetes offers many tools to ensure your applications run seamlessly in any environment. So, while this guide concludes here, your exploration of Kubernetes is just beginning – embark on this adventure with confidence and curiosity!

]]>
https://capten.ai/blog/k8s-hello-world/feed/ 0