[0:00] hey team sid here and welcome to getting [0:02] started with kubernetes this is going to [0:04] be a quick guide that takes us through [0:06] the basics of getting some code running [0:08] on our system and then deploying it into [0:10] a kubernetes cluster we're going to keep [0:12] things pretty simple and on the screen [0:14] here kind of the basic steps i'm going [0:16] to create a simple application using [0:18] python i'll containerize it i'll push [0:20] that to a container registry [0:22] create a cluster define the kubernetes [0:25] resources associated with my application [0:27] apply those resources within the cluster [0:30] and then set up the necessary dns to [0:32] actually allow traffic to reach my [0:34] application this is a video within the [0:36] broader context of my kubernetes [0:38] platform playbook series so far we've [0:40] talked mostly with theory but i wanted [0:42] to get hands-on and just show you what [0:44] it takes to get an application running [0:46] in kubernetes we're going to do things [0:48] pretty quickly today we're not going to [0:50] do everything [0:51] in the state that they will eventually [0:52] be in in that in that platform that [0:54] we're, going, to be, building, but, i, just [0:55] wanted to run through from scratch [0:58] getting some of your code running in a [1:00] kubernetes cluster rather than taking [1:02] some existing [1:03] container and deploying it into [1:04] kubernetes i think it's more educational [1:07] to actually create that custom image [1:08] ourself even if it's quite simple today [1:10] i'm going to be using a python package [1:12] called fast api to create a super simple [1:15] api application that will then bundle up [1:18] the very first thing i'm going to do is [1:19] to create a python virtual environment [1:21] now this video is not about how to [1:23] manage your python dependencies and use [1:25] virtual environments so i'm just going [1:27] to do it this basic way [1:28] by saying python3 [1:33] and the end [1:35] and i'll put that in the vn [1:37] directory [1:39] so this created a local directory called [1:41] vn and that's where it's going to [1:42] install my local packages i do need to [1:44] activate that virtual environment so i [1:46] can source the file [1:48] vn [1:49] bin [1:50] activate [1:51] and now we can see that my kubernetes [1:53] getting started virtual environment is [1:55] activated [1:56] there's a couple of packages that i'm [1:58] going to install so now i'm just going [2:00] to do pip install [2:02] fast api [2:07] we're also going to install [2:10] uvicorn that's the web server that we're [2:12] actually going to use [2:16] and so we now have those two packages [2:20] we can do pip freeze to see all the [2:23] sub packages that it may have [2:25] uh installed and so i'm just going to [2:27] create a requirements.txt file with two [2:30] things in it uh this fast api and the [2:33] the uvicorn package and hopefully i'm [2:35] pronouncing that right so i'll do code [2:38] requirements.txt [2:43] great [2:44] and the two things that i want in there [2:46] are fast api [2:48] and [2:51] unicorn [2:53] rather than pin all the way to the patch [2:55] version [2:56] i will do this [3:02] and that will allow it to automatically [3:04] upgrade [3:05] if it needs to [3:07] but only at the patch version level so [3:10] now if i do pip install [3:12] r requirements.txt [3:14] it should say that all the requirements [3:15] are already satisfied great [3:19] at this point i'm just going to go [3:21] through the [3:22] most basic application [3:25] intro here [3:27] example create it so we're going to [3:29] create a main.pi file i'm going to do so [3:32] within an app directory so i'll do make [3:34] dir [3:35] app cd app [3:38] we're going to [3:40] code [3:41] touch [3:43] init dot pi [3:47] and then we're going to open up a [3:49] main.pi [3:50] and this is going to be where our actual [3:52] application lives [3:56] and so i'm just going to paste in that [3:58] code from [3:59] the docs [4:06] i'm going to make it even simpler we're [4:07] just going to have the hello world for [4:08] now [4:10] and that means we don't need the [4:14] import from the typing package [4:18] there we go [4:22] we're going to actually run the server [4:24] with uvicorn so i'll just copy this [4:26] command and then run it here from within [4:30] that app directory [4:32] uh now it is running on port 8000 so if [4:35] we go [4:36] to our [4:37] browser and access localhost [4:41] 8000 [4:43] great we can see our application [4:46] i can change this to be something else [4:49] hello youtube [4:52] save it [4:57] uh it restarted our application and now [4:59] if i refresh the page we get the new [5:01] updated response from our api great [5:05] so that is the basic application that [5:08] we're going to use [5:10] in order to deploy to kubernetes we need [5:12] to take this and put it inside of a [5:14] container so kubernetes can run pretty [5:16] much any workload but the requirement is [5:19] that it only knows how to run containers [5:21] so rather than running this locally on a [5:23] virtual machine somewhere [5:25] we are instead going to bundle it up as [5:27] a container and deploy that container [5:29] image [5:30] as a container on kubernetes [5:33] so in order to do that there's actually [5:35] some good documentation in the fast api [5:38] page [5:39] here [5:40] about how to run fast api inside of a [5:42] container [5:44] good background here if you're not [5:45] familiar with containers i would read [5:47] that [5:48] but and if we get down to here we see [5:51] pretty much the same [5:52] code that we're using before [5:54] and then it gives us the sample docker [5:55] file so i'm going to create a docker [5:58] file [6:01] so i'll kill our server [6:04] great [6:05] at the top level i'm going to do [6:07] code docker file [6:11] and paste in [6:12] their sample code [6:17] and so if we just go through and look at [6:19] what this is doing it's taking a base [6:21] image so this is a publicly available [6:22] base image that i believe is debian [6:25] based running python 3.9 [6:28] it's setting up a working directory so [6:30] this is where inside of the eventual [6:32] system that we're deploying it should [6:34] use as the working directory [6:37] we then copy in our requirements.txt [6:39] file so that's this file here [6:41] and the reason that we copy it in [6:43] first before we copy in the rest of our [6:45] source code as we build container images [6:47] the each individual step is cached and [6:50] so by copying this in [6:52] separately from our source code which we [6:53] copy in here [6:54] we can take advantage of that caching [6:56] such that we only have to [6:58] reinstall our dependencies if we change [7:02] one of those dependencies if we instead [7:04] copied in all of our code first and then [7:05] did pip install every single time we [7:07] changed our source code we would need to [7:09] modify or rerun this step when we built [7:12] the container image [7:15] okay uh we then [7:17] run a pip install and so it takes that [7:20] requirements.txt that we just copied in [7:22] uh installs those dependencies [7:24] we then copy in our source and so [7:26] because i place it in this app directory [7:28] it will get copied in at the [7:30] root slash code slash app a directory [7:33] inside the container and then finally we [7:35] specify the command that should be run [7:37] when we actually [7:38] issued this [7:40] when we run the container [7:42] in this case it's similar to the one we [7:43] had we had before [7:45] but now we're also specifying that we [7:47] wanted to run on port 80 instead of port [7:50] 8080. [7:51] so i can do a docker [7:53] build and then i'll specify period to [7:55] say hey run docker build against the [7:57] docker file in my current working [7:58] directory [8:00] as we can see it's going through step by [8:02] step [8:03] and executing these commands [8:19] great so that container image is now [8:21] built one thing that we didn't do there [8:23] is tag it in any way and so i'm going to [8:25] rebuild it but i'm going to also add the [8:27] dash [8:28] t flag [8:31] here and i'm just going to call it for [8:33] now i'll change this tag in a bit [8:37] kate's fast api [8:39] we'll see that this time it's going to [8:40] build much quicker because we already [8:42] have all those layers cached so this [8:43] should only take a second [8:48] now we have that container image built [8:50] locally and we can run it locally as [8:52] well and so i can do a docker run [8:55] and then if we give it the name of that [8:56] container that i just tagged so kate's [8:59] fast api [9:01] but one thing i do want to do is port [9:03] forward from my local host 8000 [9:06] to con the port 80 inside that container [9:10] so up here we're telling it to run on [9:12] port 80 inside the container in order [9:14] for me to be able to access the network [9:17] inside that container i need to connect [9:19] my macbook with the host of the [9:21] container itself and so by doing so with [9:24] this dash p flag i can now [9:26] run this [9:29] it's going to run my web server inside [9:31] of that container and now i can access [9:34] it like before on port 8000 [9:39] so i refresh the page and we get the [9:40] same response [9:42] just to prove that it is indeed working [9:45] i'm going to switch this to [9:47] use an environment variable so i'm going [9:49] to say [9:51] import os [9:54] and then here let's just do [9:56] hello [9:58] and we'll do from [10:00] we'll make this an f string [10:04] from [10:05] and then rather than youtube [10:07] we'll put this [10:09] and i'll say os dot environ [10:13] dot get [10:16] and we'll look for the end flag and if [10:18] we don't find or we'll look for an [10:19] environment variable named end if we [10:21] don't find it we will return default [10:24] and [10:28] these will actually need to be single [10:30] quotes because we're inside of that [10:33] outer double quote [10:39] we'll rerun our [10:42] build command [10:47] then we'll rerun our run command [10:53] and now if we load here [10:55] we can see [10:56] we are [10:58] we didn't specify the environment [10:59] variable when we ran our container and [11:01] so it shows up as default m great [11:05] so the next step is to get that [11:09] container image into a registry where [11:12] we'll be able to use it from a [11:13] kubernetes cluster and so there's many [11:16] registries available docker hub is one [11:18] of the most popular [11:20] let me go ahead and get signed in here [11:31] i'm going to create a new repository for [11:33] this image [11:35] we'll call it [11:37] kate's getting [11:38] [Music] [11:40] started [11:43] create [11:46] and so now we're going to need to tag [11:48] this [11:50] with this [11:51] tag when we build the image [11:53] so i'm going to do build [11:54] so instead of kate's fast api [11:57] we now need to be this [11:59] and then for tag name for now i'll just [12:01] do 0.0.1 [12:09] then [12:10] i can push that tag so it exists locally [12:12] but i need to get it to docker hub so [12:14] i'll do docker push [12:16] on that tag [12:17] it now is taking the container image [12:19] that i have built locally pushing it out [12:21] to docker hub so that we'll then be able [12:22] to use it because it's a public image [12:24] anyone could pull this and use it [12:27] and [12:28] if it was a private registry we would [12:29] have to do some additional work on the [12:31] kubernetes side to make sure we could [12:33] actually access that [12:37] now if we refresh over here we should [12:39] see that the image shows up with that [12:41] tag [12:43] awesome [12:46] and so we've [12:48] created a super simple hello world api [12:51] we bundled it up as a container image [12:53] we've pushed it to a registry and now we [12:55] can create a cluster to actually deploy [12:57] this into so one of my favorite places [13:00] to deploy a quick cluster is sibo cloud [13:03] it's a kubernetes focused cloud provider [13:07] i'm going to go ahead and log in [13:11] and one of the nice things about it is [13:12] that you can launch a cluster very [13:14] quickly it only takes a couple of [13:15] minutes and so i'll do kate's [13:19] getting [13:21] started [13:23] uh we're gonna spawn it with three nodes [13:25] sure [13:26] uh we'll create it with a [13:29] new firewall [13:31] we're gonna leave [13:33] access to the [13:34] kubernetes api on 6443 open you could [13:37] lock it down so that only my ip could [13:40] access it we also are going to enable [13:42] http traffic coming in and that's going [13:45] to allow me to access this [13:47] service, because, eventually, i'm, going to [13:48] set it up so that we can access it from [13:50] a public domain [13:52] for the sizes i'm just going to pick [13:55] these small nodes i'm not going to leave [13:56] this up for very long anyways [13:58] and that looks good [14:00] in terms of advanced options i'm just [14:03] going to leave it as the default this is [14:05] networking stuff [14:06] and then on the marketplace it does [14:08] install traffic which is a ingress [14:11] controller by default [14:13] as well as a metric server [14:15] and that's to enable accessing metrics [14:17] about things running in the cluster [14:21] we're going to end up using traffic as a [14:23] way to get traffic from the outside [14:26] world into our application [14:28] for now that's all i'm gonna do i'm [14:30] gonna click create cluster and it should [14:32] create here in a couple of minutes [14:37] in the meantime i can go ahead and [14:39] create the corresponding definitions for [14:42] how we want to deploy this into [14:43] kubernetes [14:44] and so i'm not going to go in detail [14:46] into [14:47] the reasoning behind kubernetes or what [14:49] it actually is i have some other videos [14:51] that talk about what kubernetes is [14:53] whether it's right for you whether you [14:55] should self-host or use a managed [14:56] clusters i'll link to some of those in [14:58] the description [14:59] but for the sake of this video i'm just [15:01] going to go ahead and define [15:03] the resources how we're going to deploy [15:06] them [15:07] and [15:08] and so i'll create a new directory [15:11] called kubernetes [15:16] we'll, go, in, there, and, so, we're, going to [15:18] have a few things i'm going to [15:20] do [15:23] we're going to run our code in what's [15:25] known as a deployment and so [15:27] at the lowest level in kubernetes you [15:29] deploy a pod [15:31] a pod is a collection of one or more [15:32] containers that are running together [15:36] and share [15:37] certain aspects of things including [15:39] networking so you can access each other [15:40] on localhost [15:43] however you you never want to create and [15:46] interact with pods directly there are [15:48] higher level constructs that allow you [15:50] to create what's called a deployment or [15:52] a stateful set and that deployment you [15:55] can specify whether you want one or more [15:57] replicas of a particular pod [15:59] configuration and then kubernetes will [16:01] look at that deployment create what's [16:03] known as a replica set and so replicaset [16:06] is kind of in between the deployment and [16:08] the pod it's a specific configuration of [16:10] that pod and then from that replica set [16:13] kubernetes will maintain however many [16:15] copies of the application that you want [16:18] and so in this case we're going to [16:19] create a deployment [16:21] and so i'll do code [16:34] and i'm going to start from the [16:36] kubernetes documentation on the [16:37] deployments page it's going to give us a [16:40] basic deployment here [16:43] i'll just paste this in [16:46] and then we're going to change a few [16:47] fields here so their sample is running [16:50] in nginx web server in our case we want [16:53] the [16:56] the image that we just tagged and pushed [16:58] to [16:59] docker hub so i'll copy this [17:07] uh we're going to use that image [17:10] this is going to be a fast api [17:13] fast [17:14] api we don't need to name it deployment [17:17] because that's what it is [17:19] we'll [17:21] say it's in the [17:22] fast api app [17:25] we will run [17:27] three replicas that's fine [17:29] we want this to match [17:31] there [17:34] the container port is port 80 that's [17:36] where we told fast api or uvicorn to run [17:40] inside our container [17:42] and this [17:43] is telling me that i don't have resource [17:45] limits so one [17:47] feature of kubernetes is you can tell it [17:49] how many resources a particular [17:52] pod or [17:53] a container within a pod should consume [17:57] and so [17:58] we can create a resources section [18:02] and then within that we can have [18:04] requests [18:06] and then for requests we can have [18:09] cpu [18:10] and let's say this takes 200 [18:13] millicourse so one-fifth of a cpu uh [18:16] that's fine [18:17] and then for memory we can say let's say [18:19] it's going to take i don't know 300 [18:23] megabytes [18:24] cool and then for limits [18:26] i'm not going to specify a cpu limit but [18:28] i will tell it [18:29] that we don't want the memory to go over [18:32] let's say 400 megabytes [18:38] okay that should be sufficient to run [18:40] our application we basically are telling [18:42] kubernetes hey we want to create a [18:44] deployment the deployment lives within [18:46] this api version so each type of [18:48] resource will have [18:50] an api version associated with it this [18:52] just happens to be the one that [18:53] deployment lives under we give our [18:55] deployment a name and then we also give [18:57] it labels and so these labels are going [18:58] to be useful later [19:00] when we want to route traffic to our [19:02] deployment we're going to need to be [19:03] able to use those labels to figure out [19:06] which pods we want to [19:07] assign or route that traffic to and then [19:10] within the deployment we specify here's [19:12] our our specification there are some [19:14] things that live at the deployment level [19:16] such as the number of replicas again we [19:18] can use this [19:20] this this selector field is what tells [19:23] the deployment how to figure out which [19:25] pods to manage [19:26] and so [19:27] that's why we have [19:28] this match labels here and then we have [19:31] that label applied within the template [19:33] itself [19:36] this template is a template for each pod [19:40] that the deployment is going to create [19:42] and so [19:43] we've got our label there [19:44] and then we have our specification for [19:46] the pod and so in the pod like i said [19:48] it's one or more containers in this case [19:49] we just have one we're naming that [19:51] container fast api we're specifying the [19:53] image that we pushed to docker hub we're [19:55] going to allow access on port 80 with [19:58] that reports field and then i specified [20:00] the resources that this container is [20:02] expected to and allowed to utilize [20:06] so that should be sufficient [20:07] we're also then going to need to specify [20:10] a service [20:12] and so a service is is basically like an [20:14] internal load balancer inside the [20:16] cluster in front of our deployment so if [20:17] we've got more than one pod we can have [20:19] a stable internal address to [20:23] use to address those pods [20:25] and it will load balance across the [20:26] multiple replicas and so i'm going to [20:29] create a service diameter [20:34] again i generally just go to the [20:36] official documentation [20:40] go here [20:41] here [20:45] it's relatively simple to [20:47] define a service [20:49] in this case [20:51] we will [20:52] go here [20:54] paste it in [20:55] i'm going to call it fast api once again [20:59] and then we're going to want our [21:00] selector to match the labels that we are [21:03] applying to our [21:06] deployment [21:07] and so in this case i'm going to change [21:09] this [21:10] to [21:11] selector app fast api [21:13] tcp is fine [21:15] and then we're going to use port 80 for [21:18] both the port and the target port so [21:21] incoming traffic on port 80 is going to [21:23] go to port 80 on those pods [21:26] and again that makes sense because that [21:28] is the port we're listening on and that [21:30] is the port within our container that [21:32] we're running our web server on [21:42] so we've got our deployment and our [21:44] service [21:45] um [21:47] it is likely that our cluster should be [21:49] done setting up by now it's been a [21:51] couple of minutes so i'll go back to [21:53] sivo [21:54] and we see the cluster is now running [21:57] i'm going to download my cube config [21:59] file so this is a [22:02] a config file with the credentials [22:04] necessary to access the cluster [22:06] like it is warning here this [22:08] file has essentially the admin config [22:12] and so you would want to protect this [22:14] very carefully and eventually you would [22:15] set up additional users within your [22:18] cluster with more limited permissions [22:20] such that not everyone needs admin [22:22] access so i'll download that cubeconfig [22:24] file [22:28] great [22:32] then i'm actually just going to put that [22:34] alongside my application [22:37] in an actual setting i wouldn't put my [22:40] admin credentials here in this folder [22:43] but for now that should be fine [22:46] i'm just going to call this cubeconfig [22:50] sivo cubeconfig [22:53] and then [22:54] you'll want to install cubectl which is [22:56] the command line for interacting with [22:58] kubernetes and specifically we need to [23:00] tell it how to find the [23:03] the [23:04] admin config that we just downloaded and [23:06] so in this case it is up a level or here [23:10] and you can set the cube [23:11] config [23:14] environment variable [23:16] and so i'm going to export that [23:18] and that is just going to be my present [23:20] working directory [23:22] and then [23:28] sivo cube config and so now when i do a [23:30] cube ctl [23:33] get nodes [23:34] it should show me the three nodes [23:36] running in my cluster so we see they've [23:38] been up for 11 minutes we're running [23:41] version 1.22.11 [23:44] and that looks good [23:47] let's go ahead and apply our [23:50] configuration to the cluster so now i [23:52] have everything i need to do so i've got [23:54] my deployment i've got my service [23:56] and if i do [23:58] from that kubernetes directory [24:01] cubectl apply [24:03] dash f for file [24:06] give it the the local directory it'll [24:08] apply all the files within this [24:09] directory [24:12] and so we see it created both my [24:14] deployment and my service and now if i [24:16] do a cube ctl get pods [24:20] those three containers are creating it [24:22] was able to find those [24:24] in the remote cluster and so i'll do a w [24:27] for watch and it'll actually sit here [24:28] and update me anytime the status of one [24:31] of those containers changes [24:33] okay we've got one of our three running [24:35] the others should probably change soon [24:40] and now just to show you that they are [24:42] indeed running and doing the same thing [24:44] that we expected before [24:46] i'm going to go ahead and port forward [24:48] and i can port forward from my local [24:50] system to the kubernetes cluster so i [24:52] can do cube ctl [24:54] port [24:55] forward [24:57] and then we'll want to work forward [24:59] let's say to this specific pod [25:07] and i'm going to go from [25:10] 8080 [25:12] support 80. [25:15] and so this is saying from my local 8080 [25:17] i'm now forwarding to 80 inside the pod [25:20] and so before we were on 8 000 now we're [25:21] on 80 80. [25:23] so 8000 is no longer running if i go to [25:25] 8080 [25:26] that request will be forwarded [25:29] into the cluster and we get back our [25:32] default environment [25:33] one thing we can do is specify an [25:35] environment here inside the pod [25:38] and so let's say [25:40] at this level [25:42] we can say [25:45] end [25:48] we'll have a [25:49] name [25:50] and this is going to be end [25:52] and then we're going to have a value and [25:54] our value is going to be sivo [25:58] now i can [25:59] re-apply my [26:02] definitions [26:03] that new deployment will be configured [26:05] if we then look at the pods we'll see [26:07] that the old ones are being deleted and [26:09] new ones are being created with that new [26:11] environment variable [26:18] we've got our new pods up and running [26:20] and so now if i port forward again [26:22] we'll be accessing one of those new pods [26:26] except i need to get the new name [26:35] and what we expect is when we refresh [26:36] this page now instead of default end [26:39] it'll show us from sivo awesome [26:42] and so this is kind of following best [26:44] practices of keeping your configuration [26:47] separate from your application so if [26:49] you're familiar with the 12 factor [26:51] manifesto [26:52] uh [26:53] from heroku that's a key component of [26:55] that is breaking out your configuration [26:57] from your application itself and so in [26:59] this way we're able to do things like [27:00] use environment variables there's what's [27:02] known as config maps and secrets within [27:04] kubernetes where you can define things [27:06] such as your credentials or additional [27:08] configuration that will modify how your [27:11] application actually runs [27:12] and so just showcasing how you can load [27:14] in configuration at runtime [27:17] here with this environment variable and [27:19] give a different behavior for your [27:21] application [27:24] and so the the final thing that we want [27:26] to do yes our code is running in the [27:29] cluster that's great but it's currently [27:31] not accessible to the world [27:33] right so we need to figure out a way to [27:34] get traffic from the public internet [27:37] into our cluster [27:38] and so [27:39] in order to do that we're going to use [27:41] what's called an ingress [27:42] and so there's an ingress type [27:44] controller [27:54] and that is going to allow [27:55] traffic [27:57] from the public internet [27:59] it's going to hit our cluster [28:01] it's going to get routed according to [28:03] the rules that we define [28:04] it's going to hit our service that we [28:06] defined and eventually end up on one of [28:08] our pods and so here [28:10] like i said when we're provisioning the [28:12] cluster we have traffic as our [28:15] ingress controller installed in the [28:17] cluster by default [28:20] and so if we look at the documentation [28:22] for it [28:24] and take a most basic [28:29] configuration [28:30] once again we'll do code ingress.yaml [28:40] uh they have this production namespace [28:42] we didn't use a namespace so everything [28:43] actually that we've deployed so far [28:46] is in the default namespace [28:48] and so [28:53] we can see there's the default namespace [28:55] there's cube system q public and cube [28:57] node lease generally you would want to [28:59] organize your applications into [29:00] different name spaces so that they can [29:02] be isolated from each other and [29:04] logically grouped [29:05] but in this case we just use the default [29:07] namespace [29:08] here we're not actually going to [29:11] have separate paths for separate [29:12] services we could do that and eventually [29:14] you would if you had multiple back-end [29:15] services you might have slash bar go to [29:18] one of them slash food go to the other [29:20] and so this ingress resource allows you [29:22] to have a single external load balancer [29:24] and route traffic based on different [29:26] paths that you specify [29:28] in this case though we're just going to [29:30] route the route path [29:36] and the back end is going to be the name [29:38] of our service that we use which was [29:40] fast [29:41] api [29:42] we're going to route on [29:44] port 80. [29:46] i'm going to set up a [29:48] subdomain of [29:50] let's say devops directive [29:53] um so in this case i'll do [29:55] kates.devops.directive.com [29:58] [Music] [30:00] and that should be sufficient i'm going [30:03] to name this fast api as well [30:06] and so i will do [30:09] once again the apply [30:14] we created our new ingress resource [30:22] the one thing we do need to figure out [30:24] though is the public ip address of our [30:26] cluster so that we can route traffic to [30:28] that [30:31] in order to get that public ip i'm going [30:33] to go here to the admin interface over [30:36] here [30:37] we see that we have this external [30:39] ip [30:41] right here [30:44] so now i'm actually going to go to [30:45] cloudflare where i have my [30:49] dns setup for devops directive [30:52] i will log in [30:57] yeah so if i go to uh devops directive [31:00] and then let's create an a record so [31:04] we'll go to dns [31:07] we will add a record [31:09] and this record will be for kate's [31:12] and then we'll paste in the ip address [31:18] save [31:22] and so now kates.devopsdirective.com [31:25] once that propagates we'll be going to [31:28] that location [31:38] and as you can see [31:40] we just accessed that and [31:42] it took us to our application so now we [31:44] have a public endpoint that's routing [31:46] traffic to the cluster that ingress is [31:49] routing traffic to our service which is [31:51] routing traffic to our pod where our [31:53] application code is running and so [31:54] that's kind of an end-to-end [31:56] example of how to get code from your [31:59] system and run it in the cluster one [32:02] neat thing we can do [32:03] is to actually [32:04] display [32:06] the [32:06] pod name here so that we can showcase it [32:09] load balancing across those [32:11] and so the way that i'm going to do that [32:13] is within my application here instead of [32:15] just [32:16] [Music] [32:17] using this i'm going to use [32:20] hostname and so hostname is what the pod [32:23] name is going to be inside that running [32:24] container [32:26] this means that i need to rebuild my [32:29] container image re-push it to docker hub [32:32] and then deploy with that new version [32:34] and so let's just go through that [32:35] workflow real quick [32:37] uh so if i do my docker build command [32:39] i'm gonna bump this to oo2 [32:44] i was in the wrong directory so i'll [32:46] bump it to 002 [32:51] great [32:52] now i'm going to push [32:55] the new image [33:00] and this type of thing where you're [33:01] building container images updating which [33:03] one is used in your deployment ideally [33:05] that stuff's going to live in a ci cd [33:07] system so that you're not doing this [33:08] manually because it can become really [33:10] easy to make a mistake but just for the [33:12] sake of demo purposes i'm going to just [33:14] modify this here our new image is in [33:17] docker hub [33:18] and so now i just need to [33:20] reapply this [33:22] configuration so i'll go into my [33:23] kubernetes directory [33:25] and then rerun my apply command [33:29] and then [33:31] if we do a cube ctl get pods we'll see [33:35] okay two of them have already cycled [33:37] this is the old one it'll be gone here [33:39] in a second [33:41] all of them are gone and should be [33:42] running our new version so if we can [33:44] actually look at the definition of [33:45] what's running here and say cube ctl get [33:47] pods [33:49] name of pod and then dash o yaml for the [33:53] output format yaml if we look at the [33:55] image field inside of our [33:58] container here [34:00] if we can find it [34:03] here is the image and we see that it has [34:06] version 002 applied this image pull [34:09] policy is another field we could have [34:10] defined on our [34:12] deployment and that would say when [34:14] should we pull the image from docker hub [34:16] it'll for each node that is running [34:18] it'll pull that container image if it's [34:19] not present if it is present it would [34:21] use that cached version so we need to be [34:23] careful [34:24] if we're using if not present if we're [34:26] if we're not [34:27] if we're modifying a tag in place with a [34:30] new code version it could it could cause [34:31] us to not get that new code version so [34:33] it's always best practice to [34:35] treat container image tags as immutable [34:38] and always increment when you're [34:39] deploying [34:40] into a [34:41] cluster [34:44] so [34:44] now if we refresh here that traffic [34:47] should be hitting the new version of our [34:48] application and should show us the name [34:51] of the pod looks like it didn't get [34:53] picked up and so why would that be [35:00] one thing we can do to debug [35:02] is let's actually go into the container [35:04] we can get an executable environment [35:06] inside the container [35:08] uh get pods [35:12] give ctl we can do exec and then dash i [35:15] t for an interactive tty session [35:18] we'll do name of pod and then we'll just [35:20] execute bash as the thing that we're [35:23] doing [35:24] and [35:25] the actual newer command is to put dash [35:27] dash here just so we don't get that [35:28] warning [35:29] and then let me do echo [35:33] host name [35:35] so that is what we wanted to show up and [35:38] so why [35:40] when we looked in our environment [35:42] did we not get that so if i do [35:45] let's just get a repel for python [35:50] so now we're in python we'll import os [35:54] os dot environment [36:01] uh i need to spell it right [36:06] ah because [36:08] okay so i was using sort of bash syntax [36:11] we actually just want to get the key [36:12] hostname [36:14] um [36:19] print [36:25] uh oh it is all caps [36:29] great uh and so removing that dollar [36:31] sign then we'll save once again we need [36:34] to [36:35] push a new version of our code [36:38] and so as you can see [36:40] this is the type of stuff you would want [36:41] to test locally test within your [36:43] continuous integration so you're not [36:45] having to iterate and push new container [36:47] images and deploy those into the cluster [36:49] in a future video i'll get into sort of [36:51] what a normal development workflow would [36:53] look like this is just meant to [36:55] demonstrate how to get code running from [36:57] your system in the cluster [37:00] but there's much more effective [37:02] developer workflows that don't require [37:04] this sort of slow [37:06] loop of rebuilding re-pushing the [37:08] container image every single time [37:10] but for now we'll do docker build we're [37:13] going to increment it to 03. [37:20] we're we're, gonna, push [37:24] we're gonna update our deployment [37:36] then we're gonna start once that has [37:38] finished cycling [37:43] uh we are almost there cool [37:46] now we would expect our hostname to show [37:49] up and so if we keep refreshing the page [37:51] here we see that the traffic [37:53] is actually getting routed to the [37:55] different pods [37:57] as shown by the the different post fixes [37:59] there [38:00] within our cluster and so [38:02] hopefully this has given you an idea of [38:04] like [38:05] the minimum set of things that are [38:07] required to get code from your local [38:10] system and deploy that onto [38:13] uh kubernetes and so just as a reminder [38:15] of sort of what we ended up doing [38:18] this is all in the context of this [38:20] series where i'm building out an [38:21] application platform i created a super [38:23] simple api application just kind of a [38:25] hello world using fast api with python i [38:28] took that wrapped it in a container [38:30] pushed that container to docker hub [38:33] i think created a cluster with sivo [38:35] cloud which enabled me to get a cluster [38:37] up and running that that cluster had [38:38] three virtual machines running in their [38:40] data center and so [38:42] kubernetes allows me to abstract those [38:44] virtual machines away and i can just [38:46] interact with them as a single cluster i [38:48] then took my application and put that [38:51] image that i built and pushed and put it [38:53] inside of the kubernetes-based resources [38:55] that i needed and so there i needed [38:58] three things i needed a deployment so [38:59] deployment tells kubernetes how to run [39:01] my container and what configuration and [39:03] how many replicas [39:05] a service which is the internal load [39:07] balancer that allows traffic to network [39:09] traffic to be routed across my different [39:13] pods and then thirdly the ingress [39:15] resource which tells [39:18] the ingress controller in this case [39:19] traffic which was installed in my [39:20] cluster by default if you're [39:22] provisioning a cluster elsewhere you [39:23] might have to install an ingress [39:25] controller yourself [39:27] but to tell it hey for traffic coming in [39:30] from [39:31] kates.devopsdirective.com [39:33] on the root path [39:35] route it to that service that we defined [39:37] earlier and so that allows that traffic [39:39] to come external public internet hit my [39:42] cluster ingress controller routes it to [39:44] the service service routes it to one of [39:46] those three pods and then we saw it load [39:48] balancing across as i refresh the page [39:50] the different pods were getting the [39:51] traffic [39:53] and so [39:54] that's all for today uh hopefully that [39:56] was helpful if you were just getting [39:58] started with kubernetes and sort of [40:00] confused about what the steps were for [40:02] getting your code from your local system [40:06] into the remote cluster like i said [40:09] we did things manually today a lot of [40:11] this is going to be automated to help [40:14] reduce the potential for human error [40:16] we'll also talk about improved developer [40:18] workflows i know this is a little bit [40:20] different style than much of my more [40:22] scripted content but hopefully you found [40:24] it helpful [40:25] if you enjoyed this and want to check [40:26] out other videos in the series there [40:28] will be links to the playlist in the [40:30] description and that's it for today and [40:33] remember just keep building [40:37] [Music] [40:42] thank you