TubeSum ← Transcribe a video

Kubernetes 101: Deploying Your First Application!

Transcribed Jun 17, 2026 Watch on YouTube ↗
Beginner 12 min read For: Developers new to Kubernetes who want a practical, hands-on introduction to deploying a containerized application.
117.4K
Views
2.4K
Likes
105
Comments
52
Dislikes
2.2%
📈 Moderate

AI Summary

This video provides a hands-on, step-by-step guide to deploying a simple Python FastAPI application on a Kubernetes cluster. The presenter covers the entire workflow from local development and containerization to pushing the image to a registry, creating a cluster, and exposing the application to the internet via an ingress controller.

[0:00]
Overview of the process

The video outlines the steps: create a Python app, containerize it, push to a registry, create a Kubernetes cluster, define Kubernetes resources (deployment, service, ingress), apply them, and set up DNS.

[1:10]
Creating a FastAPI application

The presenter creates a simple FastAPI app with a hello world endpoint, using a virtual environment and installing FastAPI and uvicorn.

[5:33]
Containerizing the application

A Dockerfile is created based on FastAPI's documentation, using a Python 3.9 base image, copying requirements and source code, and running uvicorn on port 80.

[8:50]
Building and testing the container locally

The Docker image is built and run locally with port forwarding to verify the application works inside the container.

[11:05]
Pushing the image to Docker Hub

The image is tagged with the Docker Hub repository name and pushed to a public registry.

[12:55]
Creating a Kubernetes cluster on Civo

A three-node cluster is created on Civo Cloud, with Traefik ingress controller and metrics server installed by default.

[14:44]
Defining Kubernetes resources

A deployment (with 3 replicas, resource requests/limits) and a service (internal load balancer on port 80) are defined in YAML files.

[21:50]
Applying resources and verifying pods

The configuration is applied to the cluster, pods are created, and port forwarding is used to test the application locally.

[25:40]
Adding environment variables

An environment variable is added to the deployment to demonstrate configuration management, and the app is re-deployed.

[27:24]
Setting up ingress for external access

An ingress resource is created to route traffic from a subdomain (kates.devopsdirective.com) to the service, and a DNS A record is added on Cloudflare.

[32:00]
Demonstrating load balancing

The application is updated to display the pod hostname, and after rebuilding and redeploying, refreshing the page shows traffic being load-balanced across pods.

This end-to-end tutorial demonstrates the minimum steps required to get a custom application from a local system running on a Kubernetes cluster, including containerization, deployment, and external access via ingress.

Clickbait Check

90% Legit

"The title accurately reflects the content: a beginner-friendly tutorial on deploying a first application to Kubernetes."

Mentioned in this Video

Tutorial Checklist

1 1:19 Create a Python virtual environment: python3 -m venv vn
2 1:46 Activate the virtual environment: source vn/bin/activate
3 1:58 Install FastAPI and uvicorn: pip install fastapi uvicorn
4 2:27 Create requirements.txt with fastapi and uvicorn (with version specifiers)
5 3:29 Create app directory and __init__.py: mkdir app && cd app && touch __init__.py
6 3:49 Create main.py with a simple FastAPI app (hello world endpoint)
7 4:22 Run the app locally with uvicorn: uvicorn app.main:app --reload
8 6:01 Create a Dockerfile (based on FastAPI docs) with Python 3.9 base, copy requirements, install dependencies, copy source, and run uvicorn on port 80
9 7:53 Build the Docker image: docker build -t <your-dockerhub-username>/kates-getting-started:0.0.1 .
10 8:50 Test the container locally: docker run -p 8000:80 <image-name>
11 12:09 Push the image to Docker Hub: docker push <your-dockerhub-username>/kates-getting-started:0.0.1
12 13:11 Create a Kubernetes cluster on Civo (3 nodes, enable HTTP traffic, use small nodes)
13 15:11 Create a kubernetes/ directory and define deployment.yaml (apiVersion: apps/v1, kind: Deployment, 3 replicas, container port 80, resource requests/limits)
14 20:12 Define service.yaml (apiVersion: v1, kind: Service, selector app: fast-api, port 80, targetPort 80)
15 21:57 Download kubeconfig from Civo and set KUBECONFIG environment variable
16 23:58 Apply the Kubernetes resources: kubectl apply -f kubernetes/
17 24:46 Verify pods are running: kubectl get pods -w
18 24:52 Port forward to a pod for testing: kubectl port-forward <pod-name> 8080:80
19 25:40 Add environment variable to deployment (env: - name: END value: civo) and re-apply
20 27:42 Define ingress.yaml (apiVersion: networking.k8s.io/v1, kind: Ingress, host: kates.devopsdirective.com, service name: fast-api, port: 80)
21 30:44 Create a DNS A record on Cloudflare pointing to the cluster's external IP
22 32:00 Update the app to display pod hostname (import os; os.environ.get('HOSTNAME')), rebuild image, push, and update deployment

Study Flashcards (10)

What is the purpose of a Kubernetes Deployment?

easy Click to reveal answer

A Deployment manages a set of identical Pods, ensuring the desired number of replicas are running and handling updates.

15:25

What is the difference between a Pod and a Deployment in Kubernetes?

medium Click to reveal answer

A Pod is the smallest deployable unit (one or more containers), while a Deployment is a higher-level abstraction that manages Pods and provides features like scaling and rolling updates.

15:25

What is a Kubernetes Service and why is it needed?

medium Click to reveal answer

A Service is an internal load balancer that provides a stable IP address and DNS name to route traffic to a set of Pods, enabling load balancing across replicas.

20:12

What is an Ingress in Kubernetes?

medium Click to reveal answer

An Ingress is a resource that manages external access to services in a cluster, typically via HTTP/HTTPS, and can route traffic based on hostnames or paths.

27:42

What is the recommended practice for container image tags when deploying to Kubernetes?

hard Click to reveal answer

Treat container image tags as immutable and always increment the tag (e.g., 0.0.1, 0.0.2) when deploying a new version to avoid caching issues.

34:35

What command is used to apply Kubernetes resource definitions from a directory?

easy Click to reveal answer

kubectl apply -f <directory>

23:58

What is the purpose of the 'selector' field in a Service definition?

medium Click to reveal answer

The selector field specifies which Pods the Service should route traffic to, based on matching labels.

21:00

How can you set an environment variable in a Kubernetes Deployment?

easy Click to reveal answer

Add an 'env' section under the container spec with 'name' and 'value' fields.

25:40

What is the default namespace in Kubernetes?

easy Click to reveal answer

The default namespace is called 'default'.

28:43

What command can you use to get an interactive shell inside a running container in a Pod?

medium Click to reveal answer

kubectl exec -it <pod-name> -- bash

35:12

💡 Key Takeaways

⚖️

Deployment vs Pod

Clearly explains the relationship between Pods, ReplicaSets, and Deployments, which is fundamental to understanding Kubernetes.

15:25
🔧

Service as internal load balancer

Provides a concise definition of a Service and its role in routing traffic to Pods, a core concept for networking in Kubernetes.

20:12
🔧

Ingress for external access

Demonstrates how to expose a service to the internet using an Ingress resource, a critical step for production deployments.

27:42
⚖️

Immutable container tags

Highlights a best practice for versioning container images to avoid caching issues in Kubernetes deployments.

34:35
💡

Load balancing across pods

Visually demonstrates how Kubernetes distributes traffic across multiple replicas, confirming the setup works end-to-end.

38:00

✂️ Creator Tools: Viral Hooks

AI-generated clip ideas for Shorts based on the transcript

Kubernetes in 45 Seconds

45s

Quick overview of deploying an app to Kubernetes grabs beginners' attention.

▶ Play Clip

Dockerfile Caching Trick Explained

60s

Practical tip about Docker layer caching saves time and is highly educational.

▶ Play Clip

Kubernetes Deployment YAML Breakdown

60s

Clear explanation demystifies Kubernetes YAML for new users.

▶ Play Clip

Expose Your App with Ingress

60s

Shows real-world DNS setup and ingress configuration, very actionable.

▶ Play Clip

How Kubernetes Load Balances Pods

60s

Demonstrates load balancing in action with live refresh, satisfying and informative.

▶ Play Clip

[00:00] hey team sid here and welcome to getting

[00:02] started with kubernetes this is going to

[00:04] be a quick guide that takes us through

[00:06] the basics of getting some code running

[00:08] on our system and then deploying it into

[00:10] a kubernetes cluster we're going to keep

[00:12] things pretty simple and on the screen

[00:14] here kind of the basic steps i'm going

[00:16] to create a simple application using

[00:18] python i'll containerize it i'll push

[00:20] that to a container registry

[00:22] create a cluster define the kubernetes

[00:25] resources associated with my application

[00:27] apply those resources within the cluster

[00:30] and then set up the necessary dns to

[00:32] actually allow traffic to reach my

[00:34] application this is a video within the

[00:36] broader context of my kubernetes

[00:38] platform playbook series so far we've

[00:40] talked mostly with theory but i wanted

[00:42] to get hands-on and just show you what

[00:44] it takes to get an application running

[00:46] in kubernetes we're going to do things

[00:48] pretty quickly today we're not going to

[00:50] do everything

[00:51] in the state that they will eventually

[00:52] be in in that in that platform that

[00:54] we're, going, to be, building, but, i, just

[00:55] wanted to run through from scratch

[00:58] getting some of your code running in a

[01:00] kubernetes cluster rather than taking

[01:02] some existing

[01:03] container and deploying it into

[01:04] kubernetes i think it's more educational

[01:07] to actually create that custom image

[01:08] ourself even if it's quite simple today

[01:10] i'm going to be using a python package

[01:12] called fast api to create a super simple

[01:15] api application that will then bundle up

[01:18] the very first thing i'm going to do is

[01:19] to create a python virtual environment

[01:21] now this video is not about how to

[01:23] manage your python dependencies and use

[01:25] virtual environments so i'm just going

[01:27] to do it this basic way

[01:28] by saying python3

[01:33] and the end

[01:35] and i'll put that in the vn

[01:37] directory

[01:39] so this created a local directory called

[01:41] vn and that's where it's going to

[01:42] install my local packages i do need to

[01:44] activate that virtual environment so i

[01:46] can source the file

[01:48] vn

[01:49] bin

[01:50] activate

[01:51] and now we can see that my kubernetes

[01:53] getting started virtual environment is

[01:55] activated

[01:56] there's a couple of packages that i'm

[01:58] going to install so now i'm just going

[02:00] to do pip install

[02:02] fast api

[02:07] we're also going to install

[02:10] uvicorn that's the web server that we're

[02:12] actually going to use

[02:16] and so we now have those two packages

[02:20] we can do pip freeze to see all the

[02:23] sub packages that it may have

[02:25] uh installed and so i'm just going to

[02:27] create a requirements.txt file with two

[02:30] things in it uh this fast api and the

[02:33] the uvicorn package and hopefully i'm

[02:35] pronouncing that right so i'll do code

[02:38] requirements.txt

[02:43] great

[02:44] and the two things that i want in there

[02:46] are fast api

[02:48] and

[02:51] unicorn

[02:53] rather than pin all the way to the patch

[02:55] version

[02:56] i will do this

[03:02] and that will allow it to automatically

[03:04] upgrade

[03:05] if it needs to

[03:07] but only at the patch version level so

[03:10] now if i do pip install

[03:12] r requirements.txt

[03:14] it should say that all the requirements

[03:15] are already satisfied great

[03:19] at this point i'm just going to go

[03:21] through the

[03:22] most basic application

[03:25] intro here

[03:27] example create it so we're going to

[03:29] create a main.pi file i'm going to do so

[03:32] within an app directory so i'll do make

[03:34] dir

[03:35] app cd app

[03:38] we're going to

[03:40] code

[03:41] touch

[03:43] init dot pi

[03:47] and then we're going to open up a

[03:49] main.pi

[03:50] and this is going to be where our actual

[03:52] application lives

[03:56] and so i'm just going to paste in that

[03:58] code from

[03:59] the docs

[04:06] i'm going to make it even simpler we're

[04:07] just going to have the hello world for

[04:08] now

[04:10] and that means we don't need the

[04:14] import from the typing package

[04:18] there we go

[04:22] we're going to actually run the server

[04:24] with uvicorn so i'll just copy this

[04:26] command and then run it here from within

[04:30] that app directory

[04:32] uh now it is running on port 8000 so if

[04:35] we go

[04:36] to our

[04:37] browser and access localhost

[04:41] 8000

[04:43] great we can see our application

[04:46] i can change this to be something else

[04:49] hello youtube

[04:52] save it

[04:57] uh it restarted our application and now

[04:59] if i refresh the page we get the new

[05:01] updated response from our api great

[05:05] so that is the basic application that

[05:08] we're going to use

[05:10] in order to deploy to kubernetes we need

[05:12] to take this and put it inside of a

[05:14] container so kubernetes can run pretty

[05:16] much any workload but the requirement is

[05:19] that it only knows how to run containers

[05:21] so rather than running this locally on a

[05:23] virtual machine somewhere

[05:25] we are instead going to bundle it up as

[05:27] a container and deploy that container

[05:29] image

[05:30] as a container on kubernetes

[05:33] so in order to do that there's actually

[05:35] some good documentation in the fast api

[05:38] page

[05:39] here

[05:40] about how to run fast api inside of a

[05:42] container

[05:44] good background here if you're not

[05:45] familiar with containers i would read

[05:47] that

[05:48] but and if we get down to here we see

[05:51] pretty much the same

[05:52] code that we're using before

[05:54] and then it gives us the sample docker

[05:55] file so i'm going to create a docker

[05:58] file

[06:01] so i'll kill our server

[06:04] great

[06:05] at the top level i'm going to do

[06:07] code docker file

[06:11] and paste in

[06:12] their sample code

[06:17] and so if we just go through and look at

[06:19] what this is doing it's taking a base

[06:21] image so this is a publicly available

[06:22] base image that i believe is debian

[06:25] based running python 3.9

[06:28] it's setting up a working directory so

[06:30] this is where inside of the eventual

[06:32] system that we're deploying it should

[06:34] use as the working directory

[06:37] we then copy in our requirements.txt

[06:39] file so that's this file here

[06:41] and the reason that we copy it in

[06:43] first before we copy in the rest of our

[06:45] source code as we build container images

[06:47] the each individual step is cached and

[06:50] so by copying this in

[06:52] separately from our source code which we

[06:53] copy in here

[06:54] we can take advantage of that caching

[06:56] such that we only have to

[06:58] reinstall our dependencies if we change

[07:02] one of those dependencies if we instead

[07:04] copied in all of our code first and then

[07:05] did pip install every single time we

[07:07] changed our source code we would need to

[07:09] modify or rerun this step when we built

[07:12] the container image

[07:15] okay uh we then

[07:17] run a pip install and so it takes that

[07:20] requirements.txt that we just copied in

[07:22] uh installs those dependencies

[07:24] we then copy in our source and so

[07:26] because i place it in this app directory

[07:28] it will get copied in at the

[07:30] root slash code slash app a directory

[07:33] inside the container and then finally we

[07:35] specify the command that should be run

[07:37] when we actually

[07:38] issued this

[07:40] when we run the container

[07:42] in this case it's similar to the one we

[07:43] had we had before

[07:45] but now we're also specifying that we

[07:47] wanted to run on port 80 instead of port

[07:50] 8080.

[07:51] so i can do a docker

[07:53] build and then i'll specify period to

[07:55] say hey run docker build against the

[07:57] docker file in my current working

[07:58] directory

[08:00] as we can see it's going through step by

[08:02] step

[08:03] and executing these commands

[08:19] great so that container image is now

[08:21] built one thing that we didn't do there

[08:23] is tag it in any way and so i'm going to

[08:25] rebuild it but i'm going to also add the

[08:27] dash

[08:28] t flag

[08:31] here and i'm just going to call it for

[08:33] now i'll change this tag in a bit

[08:37] kate's fast api

[08:39] we'll see that this time it's going to

[08:40] build much quicker because we already

[08:42] have all those layers cached so this

[08:43] should only take a second

[08:48] now we have that container image built

[08:50] locally and we can run it locally as

[08:52] well and so i can do a docker run

[08:55] and then if we give it the name of that

[08:56] container that i just tagged so kate's

[08:59] fast api

[09:01] but one thing i do want to do is port

[09:03] forward from my local host 8000

[09:06] to con the port 80 inside that container

[09:10] so up here we're telling it to run on

[09:12] port 80 inside the container in order

[09:14] for me to be able to access the network

[09:17] inside that container i need to connect

[09:19] my macbook with the host of the

[09:21] container itself and so by doing so with

[09:24] this dash p flag i can now

[09:26] run this

[09:29] it's going to run my web server inside

[09:31] of that container and now i can access

[09:34] it like before on port 8000

[09:39] so i refresh the page and we get the

[09:40] same response

[09:42] just to prove that it is indeed working

[09:45] i'm going to switch this to

[09:47] use an environment variable so i'm going

[09:49] to say

[09:51] import os

[09:54] and then here let's just do

[09:56] hello

[09: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

⚡ Saved you time reading this? Transcribe any YouTube video for free — no signup needed.