Transcript
This transcript was autogenerated. To make changes, submit a PR.
Hello everybody, and welcome to my talk when Google Build is not enough.
Introduction to Bazel I'm Yershi Khabarov, lead developer at articulf,
where I help people to use Bazel. Today we will
be talking about build process and dependencies, and then we will
talk about Bazel open source build and test tool.
So let's start with build go build.
What is Go build? Go build is a command I used most of
the time for building my Go software, and Gobuild is command which
compiles the packages named by their improper along with their dependencies.
Go build takes a list of Go files as an argument and produces an
exhibit executable binary. The question is,
do we have all the go source files from the beginning?
Sometimes in our go source code we have directories like gorenarate followed
by some arbitrary binary or script. We can have any
other commands that produces some additional Go source files.
Which means as the first step of our build, we have to run
generator, generate those go files, and then
we can run go build. And this is the first set
of our dependencies where we have go packages,
go compiler and generators.
Also we can run build on different environments and platforms.
We can run our builds locally, it could be
run on cellular runner or somewhere else.
And this is the second center. The second set of
our dependencies where we have host machine is the machine where we run our build.
We have target machine is the machine where we build software for.
And we can have a bunch of environment variables that
can affect our build. Let's say we build binaries.
Now publishing we need to make those variables
available for deployment tools. Usually in my
work I plug those binaries into container images or into
target zip archives to use it with AWS lambdas.
And as well as I need to provide some yamls,
some yaml manifests like kubernetes manifests,
helm charts, or cloudformation templates with instructions of how
to deploy those artifacts. And this
is a third set of our dependencies where we have docker to build
container images, customize weight, or any other Yamuna
operating tool, AWC, CLI,
etcetera. So what is the dependency?
Technically, everything involved into a build process or anything can affect
our build process is a dependency. How to control our dependencies
for go packages we can use go mod and go sum files.
For go compiler we can specify version inside the
container image we run built inside, or we can use just a
random Go version installed on host machine for
generators, well, it depends on generator.
For platforms we can use build flags or we can run build on a specific
platform for environment variables. We can specify
them explicitly during the build. For tools like
Docker, customize and YT, well, we can just install.
What else we should consider during the build process we
have to think about is
our build reproducible and is our build
well validated and hermetic? Sometimes we can use docker
images for hermeticity and run our build inside
a docker image. And when build fails,
can we start the build from the failure point but not from the beginning
to save our time? The output for our build process
will be artifacts. Among them we will have go binaries,
container memories or tarzeep archives.
We have to know that we are going to build and publish ready
to go artifacts, but not to deploy them.
So yobuild is not enough. Gobuild is
just one step of the process. While we actually need
in the build architect session, let's define our problem scope.
So we are going to automate our build process. And by automation
I mean as a result we will have one documents for
build and publish our artifacts. So this
process will include downloading and starting all the
necessary dependencies, including generators, compilers, tools,
etcetera. In that during this process we will build artifacts
and then we'll publish those artifacts to ECR three
or somewhere else. Also, we'll try to
make this process as little as possible and as reproducible
as possible, which means we're going to pin all the versions for
all the dependencies we've defined a build process.
Let's talk about Bazel. Bazel is an open source built
and test tool that uses human readable high level build language to define
build in a declarative way, which means to build anything with
Bazel we need to write a build configuration.
Bazel is aimed to build large codices. So Bazel was
created at Google. So when we talk about large cool basis,
think about Google scale. Bazel supports multi language
and multi partner builds. Definitely. We can build with Bazel
many different languages, and Bazel
unifies build approaches across multiple languages and multiple tool
chains, which means with one Bazel build
command we can build applications in written in
different languages. Let's talk about what Bazel can do.
Bazel can build software and it does it in parallel way.
Bazel uses as many codes as it found. We can run
build with Bazel locally or remotely, which means
if our project is large enough, we can spin up remote
runners and build run build there.
We can build everything from sources, including dependencies,
but it caches all the download dependencies in intermediate build results,
which means subsequent build will be much much faster and by
the changes in sources and rebuilds
change parts only, which means again subsequent builds,
even for changes source code will be much faster,
much faster than let's talk about a
couple of important principles. When given the same input,
source code and product configuration, a hermetic build system
always returns the same output. Hermetic builds
are insensitive to libraries and other software installed on the host machine,
which means if we take our build and move it across
different machines, the output will be the same
for the same input. Source identity Hermit
implement system try to ensure the sameness of inputs by using checksums
to identify changes to the build inputs.
As we will see later in our examples,
we'll have checksums for all the dependencies
we use. Sandboxing compilers and other
tools during the build have the access to explicitly
defined inputs only, which means first of
all, we need to define all the inputs explicitly,
and if we don't define them, build will not see them.
Okay, it's demo time. For our demo,
I have simple hello world generator. This program
will print which we can compile and this
code could be found by the link below. So how can
use it? As a first step, we need to build and
we'll have a binary. Then we can reference this
binary from, let's say go generate director.
Then we will invoke go generate and create a
file with the following content. Then we will
go we will run build for this newly
newly generated file and then we can so
let's see how it looks, how it works with Bazel. With Bazel,
we'll have just one invocation Bazel run followed by the label.
So what we have here, Bazel is just a binary
run is a Bazel subcommand. Then we have a
label. Label is a unique name for a build target and
this label consists of several parts. First one double slash
defines a product, double slash go
defines a package and column hello world
is a build target. And now let's look how this product will
be modified with Bazel. So as you can see
here, we don't have any go files, but we
have other files. First one is worthless file. It defines
portal define referenced by double slash and
this file may contain may contain external dependencies.
Also have build files build files define the package like
double slash or root package or double search go package
build files declares zero or more build targets in
most cases for goprojects, build packet structure is
the same as directory structure. As you remember, I've mentioned
human readable high level build image. And this is Starload.
Starlock is a subset of python, and it's limited to
express configurations. It's not, it's not intended for
writing applications. So here is our workforce file.
A role set is in a session for Bazel. It's like a plugin that allows
Bazel to build different software. In our case,
rose Go will be our rule set that allows Bazel to build
go software. Then within this file
we explicitly find go version and this go version
will be downloaded by Bezel. Also in the end of
the file we have a gorepository dependency which references
our hello world generator. As you can see,
all of our dependencies have sum or
checksums. Let's look at build files.
So build files can have zero or more
build targets. Build targets will be defined by rules.
So rule is a function implementation. It takes an
input and produces an output. We have three rules
here, go library and gobindrary.
All those rules define targets. Target is a buildable
unit. The first rule called generatehallogo,
it creates a hollow to go file by invoking our
hello world generator generate file
and creates a library. And third world go Binary
takes our library and creates an executable
binary. So what will happen when we
execute basel run followed by the label?
When we call basel run followed by the label,
Bazel will know that it have to build binary first,
and to build binary you have to build library first, and to build library you
have to generate a loadable file. So when we run
Bazel run command, all this,
those builds happen automatically. Let's look at
another demo. For this demo I have a bunch of mic services
deployed into Kubernetes cluster and running with stilt.
So we have here service one which is drape service which
is listening on port 5000 and it's mapped onto port
55,000 on localhost. Then we have
service Oz. This service will be authorizing all our
coming requests. And third service is envoy proxy
that acts as an paid gateway port 8084
and wei proxy mapped into allocost
and it accepts HTTP requests in a boy will translate those HTTP
requests into JPC requests and we'll call our service one.
I'm going to build all the binaries,
container images and kubernetes manifests with
one command and deploy them into Kubernetes cluster.
Let's see how it could be done and deploys all
these services. I run built everything in the
elements to save a time with zero service
available, which is fine. And we need
some time to propagate these services so we can
use drapes URL and hit the service drape service directly.
Now let's call it again. If I change
this token to invalid one, we'll have
authorized request. Then I can and
this command will stop tilt delete all this,
delete all the pods and delete namespace where
it was deployed. As we've seen, everything was
done with just one comment. Basel run when we work
with Basel, we reference all the build targets
by labels. Labels could be different.
So what are we gonna do with labels? The generic format
for the label is followed by path to the package
column and target name. And this is our
so called internal labels labels within our project we
can with those labels everything recursive.
For example, we can run bazel build double triple
dot and. And that means we build
everything under there special called all
which is the same as triple dot. We also can build everything
recursively under one package. How can build packages
the same with triple dot or old target? Also we
can generate. Sorry. We can build external
dependencies and in this case path labeled
external dependencies start with a sign. We also can
build one target or we can run one target.
Should we write those build files manually? So if we have
quite a big project, we will have elderly
build files, one file per package or per directory.
Unfortunately, we don't need to
run them manually from scratch. There is a tool called Gazelle
which can generate build files for us.
Also it can keep them up to date and format build files.
Also Gazelle manages all the dependencies.
As for other targets like for container images,
yaml manifests and publishing artifacts. Yes,
those targets we have to write manually, but it's for
building stuff. What else we can do with Bazel?
We can ask Bazel some questions like use package
auto x which depends. If package X has
with the same baselo query, we can visualize our dependency here like
this. This one is a validation for
one target and this container
image includes just one go binary.
Bazel is accessible. So as I mentioned before, we have rowsets
which is an extension or plugin for Bazel.
And there are different row sets like Rose
go for working with building go
applications, roS OCI for building containment images,
Ros Proto for working with Protobuf, etcetera.
A lot of different information about existing
rule sets you can find on awesomebyzel.com website.
And as for more info about Bazelo, you can look at Bazel
build website and mentioned above awesome basel.com
and that's it for today. Thank you for your attention.
Happy building.