Transcript
This transcript was autogenerated. To make changes, submit a PR.
Are you an SRE,
a developer?
A quality engineer who wants to tackle the challenge of
improving reliability in your DevOps? You can enable your DevOps
for reliability with chaos native. Create your free
account at Chaos native. Litmus Cloud hi,
welcome to Comp 42 dot Com's SRE 2021
conference. We get to talk about level up your DevOps with GitHub
actions in kubernetes. Here's the part where I tell you that I'm going
to post my slides, but there are no slides.
This is it. That GitHub,
that notepad document. That's it.
Now you can go grab the code for this. It's online right now here on
GitHub. Yeah, we need to go build this code.
But if you didn't grab this URL, go to robridge.org
slash presentations and here's level up your DevOps.
The code is online right now, and once we finish build it,
it will be up there on GitHub while you're here@robrich.org.
Click on about me and you'll see some of the things that I've done recently.
I'm a Microsoft MVP, a friend of Redgate, a docker captain,
and a cyral developer, automate AZ give camp
is really fun AZ give camp brings volunteer DevOps together with
charities to build free software. We start Friday after work.
Sunday afternoon, we deliver the completed software to the charities.
Sleep is optional, caffeine provided. If you're in Phoenix, come join
us for the next AZ give camp. Or if you'd like a give camp in
your area, hit me up on email or on Twitter and let's get
a give camp in your neighborhood too. Some of the other things that I've done,
I do a lot with containers, consulting and training,
and one of the things I'm particularly proud of I replied to
a Net Rocks podcast episode. They sent me a mug.
Yeah, that's cool. They read my comment on the air. And there's my coveted net
rocks mug. So let's dig in. We have here
all of the code that we need to yep, we've got an
empty folder. So our first step in building up a DevOps
pipeline for containers is to have something to include
in our container. Let's do that. So let's start out
by creating a new website. Net new.
Net New allows us to scaffold out lots of different types of websites
in. Net. And so we could do an MVC site, a website blazor
angular react. Those are all great projects. Let's do this.
Net new MVC
and we'll give it a name of levelup DevOps
and an output folder of the current folder. If we didn't
give it a name, it would use the name of our folder, in this case
demo. And if we didn't give it an output folder, it would create a new
folder inside this folder for that content. So we
created it in the current folder and now we have some content.
Let's open it up inside vs code. Now we've
got controllers. Let's close all
this content. We've got controllers, we've got views.
Here's our home view. And it says welcome
to comp 42 SrE 2021.
Woohoo. Now we've got our website. Okay,
now we need to get our website into containers. Our first step is
to worry about git. So let's first create a git
ignore file. Git ignore.
That isn't it? Git rename
git ignore. There we go.
Oh, do we want to add assets?
Yes. Now it's going to add for us this
vs code folder. And this vs code folder will
give us the things that we need to be able to debug our application.
Okay, back to our git ignore file. Now here are all
the things that we don't want to commit to our repository.
So our bin folder, our OB folder,
those built assets, also user specific files
like Star user Star Suo,
the vs folder. If we were using visual studio,
and that's a pretty good spot for removing content
from git. We could also include the star
log or Star
TMP, any other files that we wanted to exclude. But for now,
this list is pretty good. Now we
want to be able to create a Docker ignore file
that has the exact same syntax as our git ignore file. Now this
docker ignore file docker ignore is
the exact same syntax. So in fact we can start with
our git ignore and just use that as a docker ignore.
In fact, if we don't have a docker ignore file,
but we do have a git ignore file, then docker will use
that instead. Now this is everything. We don't want to
end up inside of our container. So we've
got bin and obed star user star suo.
But we also want debug content. So for example,
appsettings development JSOn.
And here in the properties folder, launchsettings JSOn.
Now launchsettings JSON is all of the things that
we need to be able to debug our application. So should we start?
Kestrel, what port do we want to debug on? And those aren't necessary
inside of a production runtime container. Okay,
now we've got our Docker ignore file. Next up,
let's create a docker file.
New file docker file.
Now it's important this be named Docker file and
not Dockerfile txt. If you
did accidentally create it with Dockerfile. Txt,
easy enough rename and just make it Dockerfile.
Now I have the docker extension installed in vs code so I can
see the whale. It turned into beautiful colors. That's perfect.
Now let's build up our configuration as code that explains
how to get from our source code to a running container inside of
kubernetes. Now we'll start off with the
various lines for a docker file from copy
run CmD.
These are the main commands inside of a docker file.
In fact, if you know these four commands, you can probably read pretty
much any docker file and understand what's going on.
Let's start off with from now this specifies the base
image that we're going to build on top of. We want to stand on the
shoulders of giants so let's head up to Docker hub and take
a look at the content. Now I'm going to search for. Net and land here
on this. Net image repository.
Now it's a meta repository. It shows me where the other images
are. Now we can go to. Net samples. That's great for understanding
various docker files that build. Net in interesting ways.
Net monitor is pretty cool. Net runtime depths that's
the base content that we need to be able to install.
Net on top of. On top of that is the. NET runtime
which will include content for running console applications.
On top of that is ASP. NeT which will allow us to host web servers
and on top of that is built the SDK, the build tools.
Now we want to build inside of our container. So let's start there.
Here inside of Docker Hub with the.
Net SDK here's the image that we want. We're using
net 5.0 so that's the correct image
that we'll use as our base image. Scrolling down a little bit we can
see that the various tags available
and the various docker files used to build those tags.
And if we were just using the 50 tag that
was specified above, we'd end up on Debian. Now Alpine
is a really really small version of Linux so let's use the 5.0
alpine tag instead. Okay, so back here in
our Docker file we're going to say from thatalpine.
Now we're using the 50 alpine tag that includes the build tools,
the SDK, and now we have all of. Net in place.
Our next step is to copy all of the content here from
our folder. So we'll copy all of the content from the folder
where we run the docker build command into the
current folder in our image. Well, where is that current folder?
Let's specify that workdur is Src.
Now there's nothing magic about src. We could put it in Varlib
wherever we wanted it to, but in this case I'll just have it
at src, and that's a good spot for it. Now it'll create that directory
and change into it if it doesn't exist.
So we're copying all of our content into that Src directory,
and now we need to run some build commands. Now if we were
at the command line, the commands that we would run to be able to do
this, we would say net
restore. That would restore all of our nuget packages. Net build
will build in release mode. Net test
that will run all of our tests will also do that in release mode
so that release mode so that we don't accidentally
get debug and release content in place. And then
net publish and we'll publish to
the disk folder. Now NEt publish is that
same command like right click publish inside of visual studio,
but we're doing it here inside of our build process. So it'll
be identical every time. It will go gather all the HTML CSS
files, all the Javascript files, and also all of those
built dlls. But it will leave behind all of our CsHTML
and c sharp files. Okay, so here's all
the commands that we would run from the command line, and we need
to run them as part of our docker build.
So let's run this one and run this one and
run this one and run this one. There we go.
Now we've got our docker build file executing
each of these tasks. Now each of them is going to build a separate layer,
so we could do interesting things like combine
them together. But in this case we really like
that. Docker will cache each of these layers, so having them specified
here is kind of nice. Next thing we need to run this.
So we need to say net levelup
DevOps Dll and that is in workder
dist. Now there's a few more things that we need
to copy into place. Let's go grab some environment variables.
Now this says we should run in production mode and we should run on port
80 and we'll add some additional metadata to our container
so that we know to hook into port 80.
Perfect. Now we could use this build file as
is and it would work great. But there's a few things that we can
tidy up here to make our production runtime
image a little bit smaller. Right now we're including our
source code and our build tools in our production image.
Let's see if we can go grab some content here on the
Docker hub for ASP. Net and see if we can run just
the website. Now this Asp. Net base
image will allow us to just run the website. It doesn't include
build tools and scrolling down a little bit we can see that it also has
an alpine variant. So let's come back into our Docker file and
let's split this in half. Let's go here alpine
now we have now two images that we're
building with one Docker file. We'll call this one our
build server image and this
one will be our production runtime
server image. And I put server in air quotes
because well it's not actually a server,
it's an image but we can think
of it kind of like a server. This top part will do all the building
and then once we're done building, this bottom part will only include
those pieces that are specific to our application.
Now we do need to let's switch this to an
app folder. We do need to copy the content from the disk folder
into this folder so that we can be able to run it this way.
But right now it's going to copy it from the disk folder on my host
machine, not from this other container. So let me say,
I will say as build.
No, from equals build.
Now I've said from equals build. So it's going to go look
for the build stage. So let's name this one as build.
Now we could also name this one as
prod or whatever but we're not going to use that label so that's fine,
we'll just leave it off. But this very specifically says copy from
the disk folder in this build image into the current
folder, this app folder here on this new image.
That's great. Now that we've split our file into
a multistage build, this stage will include all of our source
come and build tools and this stage will not.
The next thing we can do is take a look at these layers.
Now we're going to copy in all of our content and then we're going to
run restore to grab all of our dependencies and then we're going to
build. Well what if I just change a JavaScript file?
I'm going to re restore my dependencies.
Really? I want to restore my dependencies
first and then copy all my files.
Well, I still need to grab my manifest, so let's
copy levelup DevOps Csproj into
the current folder. Then we'll net restore, then we'll
copy everything else and then we'll build, test and publish.
Now that means if I change just a cshtml
file or just a JavaScript file that I'm not going to
re restore my nuget packages Docker will continue caching
this layer, it'll start here, invalidating this layer and
it will start our build from here. Now if our build fails,
it'll stop at this step. If our tests fail, it'll stop here.
If our publish fails, it'll stop here. And so we'll only get to a production
runtime image. If we restore all the things correctly and
we build correctly and all our tests pass and we're able to
publish. But if something fails and
we're not changing the dependencies that the packages we depend on,
then we can start here copying all of the new files and continuing
on. Perfect. Now we have a docker file
that is great in being able to build our content, cache the layers correctly
in docker and separate it into this multi stage build
that allows us to have a really lightweight production runtime
container. Now our next step is to
create a Kubernetes Yaml file.
Now Kubernetes Yaml files can get kind of big,
so let's just go grab the file that we used last time.
Here's the file that we used last time. We've got a deployment
that specifies the number of replicas. In this case we're only going to run one.
We have a service that will load balance across all of those one
containers. We have an ingress that will grab our content
and resolve DNS and point it at the particular service.
But we do have some things that we can replace here.
Here's our AKs URL that will work on DNS.
Here is our image label, our Githash.
Here's our container registry URL. We do
need to replace last time, so let's replace this with
levelup DevOps and we've got that
in place. We could also tune other things like
the number of replicas, the resource limits,
any other dependencies that we want. But at this point I think this one works
pretty well. Okay, so we've got now our
docker file that will build our content, our Kubernetes Yaml file that will deploy
it to Kubernetes. And let's get all of this up to GitHub.
Switching over in vs come I will stage all of these
changes and I could pop open each file
inside of vs code and see the old and new.
But every file in this is new. So far they're all
added. So I'll just look through and make sure that I don't have any
files that I didn't want to commit. Did I perhaps forget to
exclude the bin or Obs folders? No, it looks good.
Initial commit,
perfect. So now that I've got that commit in place
git status, we can see that we've got that in place.
Let's get push origin main and push
that up to GitHub. Now if you grab this
URL up on GitHub, you can see that we've got the code
in place and it's looking really nice.
Now this was a pretty much empty GitHub repository,
but I did do a few things on my way in. I went here
into settings and I went to secrets
and I created a few secrets that we can use in our GitHub
actions build. What is our containers, registry password URL
and username, what is our Kubernetes URL and
the kubeconfig that we need to be able to log into it. So let's
use those secrets to build up our GitHub actions.
Now when we click on actions for the first time, it will give us lots
of templates that we can use. So if we want to do an openshift or
Alibaba cloud deployment or terraform
rust, there's lots of integrations that we can get
to here. That's pretty cool. Now it did guess that this
was a Jekyll site, but in this case I think
I liked the publish a docker container workflow
instead. Now we could definitely start from a blank file as well.
It's just a docker file. And I'm going to rename
this to Docker Kubernetes.
Okay, so here's the GitHub actions build script
that will allow us to get the content into place. Docker build
and push Kubernetes apply.
Now in this case I'm not going to run it on a schedule.
We'll come back to pull request. And so in this
case I'm only going to run it on the main branch.
Perfect. Now I'm going to remove these environment variables in this
case and let's get straight to the content that we want to do.
I'm going to delete this and
this and this.
And let's create a new stage.
This will be name
Docker build and push Kubectl
apply and let's run
this script. Now the script that I
want to run is first off I'm going to say Docker,
Docker build.
That's the current folder where I want to start my building. And let's
tag this on our way through as well. So I'm going to tag it with
levelup DevOps. And now
let's go use some of those secrets. I want to be able to grab that
GitHub, the secret here
for my registry URL.
And so taking a look at how secrets work inside
of GitHub, we've got our secrets here, they're all in place.
And now we can use this dollar curly Curly
to be able to get at that secret.
Perfect. So here in
my GitHub actions build file, let's say dollar curly query.
And I'm going to say secrets acr
URL. There I've tagged it with my container
registry so that when it comes time to push it, it knows exactly
where to push it. I'm also going to give it a version. In this case
I will use GitHub Shaw, which is
the GitHub hash associated with this commit. Now we
could definitely grab something like a build number or other
details. This just makes sure it's unique and allows me to
quickly go from the container running in production to
the source code that created it.
Okay, so I've got my container built and it's going to do all
of those steps inside the build file.
Once I've got my image built, let's push it.
So docker push and I will go grab this
image and I will push that up to
my docker registry. Now what's that registry? I think
I probably need to authenticate into it first. And that's a
great place to go grab some content from the
marketplace. So let's do this. Let's do a
docker login. Now I need to log into
my registry. So here's one.
And is that the one that I'm after?
Yeah, that one will work.
Now here's the yaml associated with it that I'll need to include.
I could pull into the full marketplace listing and take a
look at the details. It's not uncommon to get from there
into the GitHub
repository that includes this content. But that's
the action that I need to grab. So let's grab this yaml
and let's set it in place right here. I will need to indent
it a little bit to get that lined up. There we go.
Now we need to log in to our registry.
So what's our registry? Well we happen to have a secret for that. So let's
say secrets Acr
URL. There's our registry URL.
What's our username? We have a secret for that as well.
Let's do secrets Acr URL.
No, Acr username.
Perfect. And what's the password?
Let's go grab the secret for that as well. Secrets ACr
password. There we go. And then
log out. Now this is optional and
the default is true. This will log
out not at the end of this step, but at the end of the entire
build. Now if I'm on a shared build agent where
I don't necessarily have control over that content, then I definitely want to
clear out these secrets after the build. With GitHub actions
hosted on GitHub they automatically purge the vm that kicked
off my build so I don't need to worry about it. But yeah, it's a
good idea just to clear the secrets on the way through. Perfect.
So we'll do our docker build, we'll push that up to our container
registry. The next step is to kubectl apply and
I'll give it the k eight s Yaml file that we built.
Now here in this k eight s Yaml file is a bunch of the secrets
that we needed to replace Acr URL image label.
So let's go replace those. Now this said command
is a little swirly, so let me just copy it into place.
Let's go do this replacement. Now here in
the Kubernetes KDES Yaml file we're going to
replace the content, not back it up. We'll go grab the Acr URL
and replace it with that secret. We'll go grab the aks URL,
replace it with that secret and go grab the image label and
replace it with that secret. And we're going to do this globally.
So we'll replace all of the instances here inside
our Kubernetes yaml file. Now I'm very specifically doing
this after I pushed it to the registry because
if I were to do it earlier, like maybe here
and then say for example, I forgot to exclude
that from my docker, ignore. So yes, we forgot
KDEF Yaml here, then those secrets
would get embedded into my container and I
really don't want to do that. So I'm very specifically going to
put these secrets replacement after I've pushed
my container up to my registry. Now I know that even if that Kubernetes
Yaml file leaked into my image that it doesn't include those
secrets. Perfect. Now we do need to
log into our Kubernetes cluster to be able to do a kubectl
apply. So let's head back out to the marketplace
and let's look for a Kubernetes Kubectl.
No, kubernetes set context.
There we go. There we go.
Kubernetes set context. Let's go grab this one.
Yeah, that'll work. Is this one the one
that I'm after though?
No, I did want that one. Here we go. I should
have looked at the stars. So let's copy this
one and we'll set it in place. We probably need to adjust the
yaml to get that to line up. Yep, there we go.
Now here with Kubernetes set context,
our first stop is with the method.
So let's remove some other things and we are
going to use the cubeconfig method.
Now because we have the cube config method we need to specify the cube
config. Good thing we've got a secret for that.
Secrets, secrets cubeconfig.
And I grabbed that by grabbing the cube config on
my local machine. I went into my user profile
directory, grabbed the cube folder and inside
there the config file and extracted that portion referencing
this particular cluster. So with that cube config
in place I don't need the other ways to authenticate to it.
So let's remove those. And now I've got
my build in place. So we're going to start out checking out
our content. Then we will do a docker login logging
into our docker registry. Then we'll do a Kubernetes
login logging into our Kubernetes cluster. And then we'll do all
the steps associated with our build. We're going to build
our docker image doing
all of the steps inside of our docker file. We will push that
resulting image to our image registry. We will replace
all the secrets inside of our Kubernetes yaml file
and finally apply that Yaml file into kubernetes.
That looks great. So with this in place
let's start the commit and this
will be create GitHub actions build.
Perfect. Now this just puts a
file inside of our repository, inside the GitHub workflows
folder. And we named the file Kubernetes
Yaml. It's just a regular file inside of our
build. So if I come in here and I say git pull,
then we can pull that down and get at that content here
inside of vs code as well. So if we need to
adjust this, we can just pull out content. So in
this case I added an extra comment. We can remove that and
we're doing just fine. Oh, I've got an extra line there too.
Now because we have a folder inside GitHub workflows,
it kicked off a build. So let's come back here and we can see this
build is running. There's our orange dot and we can
pop open the details of that build. Now it's
going to do each of those steps. So it checked out
all of our content, did the docker login Kubernetes set
context. Now it's digging through all of the steps inside of our docker
file. Finally, once it gets done with the docker file, it's going to
push that up to our container registry and then finally do that
content of getting it into kubernetes. So it
looks like it just finished pushing it up to our container registry. And there's
our Kubectl apply and we can see that our
build is green. Very nice. Now let's do
a cubectl get all and we
can see our service is starting to spin up. If I hurry
I can find it starting, but it's already running perfect.
Now it does take a while for the DNS to propagate associated with
our ingress controller. And so as a shortcut for that,
there is a mechanism where we can use port forwarding to jump straight
into the content. Now we could port forward into a specific
pod or into a deployment or
into a replica set or into a service. Let's do that.
So here I'm going to say Kubectl port
forward servicelevelup
DevOps and I can forward port 80 on my local machine to
port 80 inside the container or inside the service. But maybe I've
got something else running on port 80 on my machine. So I'll use port 80
80 instead. Okay, now with that set
up I can come here into localhost
80 80. And I can see that we've got our
website online. Welcome to comp 42 SRE 2021.
Woohoo. Now it did all of those steps to get
from our source code all the way to our running container
inside of kubernetes. Now we did skip
a few steps. It could be nice if we built a readme
here and we did skip over pull requests,
so let's come back to that. Now first up
let's build a reaDme. I'm going to build a readme
MD and let's go grab some content
for that readme. Here's some readme
content. And wouldn't it be cool if we could put
a status badge here showing the status of
our builds. Let's come back here into actions
and pick our build and let's
create a status badge. I'll copy that content into place
and let's paste it right here.
There we go. There's our status badge. Now let's
take a look at the content that we've got. Ready to go?
Let's stage all these changes in our docker ignore file.
Oh yes, we forgot to ignore the kdes yaml file.
Here's our new readme file and we
modified a few things here. So add
readme and fix
typos. Let's commit that
and then push that into place.
Git, push origin main, that will send that up
to GitHub. And because we have that content
in place, it's actually going to kick off a new build. Let's watch it because
it's really fun. Last time we went in here and we went to details to
get out our build. This time let's go to actions and we can
choose our particular workflow. Or I see the build started right here.
That's great. Let's dig into that particular build and watch
it go. So the first step is to Docker login and then
Kubernetes login. It did both of those. Now it's pulling down the dependencies
and starting into each of those steps inside of our docker file.
Our first step is to copy the
CS proge and then run net restore. Looks like that
worked out great. Then we'll copy in everything else and run our
net build. In this case our build succeeded. That was wonderful. Let's run
all of our tests. There are no failing tests because there are no tests.
Then we'll run our net publish, finally copying that into
our new production runtime image and
push that image up to our registry. We're back in
our regular docker script and with that in our registry then we
will push that up to Kubernetes and we have the latest version
running inside of Kubernetes. Perfect. The other thing we
have now that we have this readme is this
status badge that will get us straight to the build results associated
with that build. Perfect.
Now the other thing we wanted to take on is to do pull requests.
So let's close this and
close this now here's the build
that we built so far. We could definitely come in here
to GitHub and we could click on edit
and get back into the designer where we have the marketplace and we
can edit the things. But in this case let's just modify it
as a file here in our repository. Now I want to
build a new workflow for DockerPr
Yaml and this will run for
pull requests.
So here I want to do the docker
build for prs.
Now I want to run on all the branches that are not
main in case I do pull requests based on other branches.
And then I also want to say pull request and
branches. I will do this on
all branches. So if I do a pull request from any
branch I will kick off this run. And if I commit to
any other branch then I'll also kick off this run. Now as
part of doing a pull request. I definitely don't want to deploy to
my production kubernetes environment. So let's delete that step
because I'm not deploying. I don't need to put
secrets in place because I'm not deploying. I also don't need to log into
kubernetes. Let's delete that step. I also don't
want to push the resulting image into my container
registry. Let's delete that step. Now I also don't need to log
in, so let's delete that step. Docker build
APR and now I don't even necessarily
want to tag it with my container registry. I'll just say
level up DevOps PR there we go.
Now we have a build suitable for pull requests.
It will build on all branches that are not main. It will build on all
pull requests and it will only do all of the build steps inside of our
docker file. Now this still will do all of the build,
test and publish, make sure that our image will
build successfully. And if we have any failing unit tests
or if we have any syntax errors in our source code, then our
pr will definitely fail. Okay so
let's commit this content to our registry
and we will say PR build and
let's commit it and push that up to
git push origin main now
we changed our main branch. We didn't change a pull request. So because
we changed our main branch it's going to kick off a new build.
Perfect. This is so much fun to watch. It's going to
do exactly those steps. It will do all of the steps
inside of our build script. And so
our first step is to log into Docker and Kubernetes. The next
is to start enumerating through all of our steps in our docker file.
The first step is to go grab all of the
base image and then we copy in all of our content.
Net restore. That net restore worked really well. We'll do
a net build. Let's see if our source code worked. Yes, no warnings,
no errors. That's great. Let's run all of our tests, no failing tests.
Let's publish. Then we'll copy that content into our new
image. And now we're done with our docker file.
And the next step is to push that image
inside into our containers registry and finally start that new copy
inside of kubernetes. That worked.
And if we now come into our actions,
we see that we not only have that build that we just did, but we
also have a new build for pull requests. Perfect.
Now what if we want to do builds for other environments? Maybe a
dev test environment. So we could do a similar thing.
We could come in here and we could duplicate this
build, switch this from main to develop
and start replacing these. Maybe this is the ACR URL
for develop and the
username for develop. Well that gets a little
crazy. Let's instead use GitHub
environments. So we're going to modify this same build,
the normal build that we want to do our things and it will be on
the main branch and the develop branch,
which means we need to come here in our pull request and say,
but this is also not
develop. And now let's set up our
GitHub actions secrets for environments. So I'm
going to come here into settings and I will come here into secrets.
And right now all these secrets are not associated with a particular
environment or branch, but I can create environment specific
secrets. Now that's perfect. If I create environment specific
secrets for each branch, then I can put the develop branch secrets
that have the exact same names with the develop
specific values. Now I only need
to have one build file that will use the exact same secrets
as all of the details. But because I have five different secrets,
I can now push to my develop environment with
that exact same build script. So let's
commit this content. We've got our,
we'll stage our change to our build file that adds
the develop branch and our pr build that removes
the develop branch and add a build
for develop. Now because we're using
environment specific secrets, we can do all
this with one build file.
Now we just kicked off a build. So actually,
let me git push origin main we just kicked
off a build and we've seen that work a bunch of times.
It is really fun. This was really cool getting
to show you GitHub actions and how you can level up
your DevOps for containers with Docker and Kubernetes.
You can grab the code that we just built up on GitHub@GitHub.com.
Slash Robrich levelup DevOps, GitHub actions,
kubernetes or go to robrich.org, click on presentations
and it's right there. Find me at that spot where the conference is designated
for live Q and A. Or if you're watching this video later, hit me up
on Twitter at Robrich. And this has been so much
fun getting to show you this build. Thanks for
joining us at Comp 42.
At it comp 42
Sre 2021 conference
thanks.