Skip to content

Commit a81a4cd

Browse files
committed
fix: proofread post
1 parent 4f9fe9e commit a81a4cd

File tree

2 files changed

+30
-30
lines changed

2 files changed

+30
-30
lines changed

_posts/2025/01/30/2025-01-30-ci_overview.md

+30-30
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
22
layout: post
3-
title: "Github Actions CI"
3+
title: "Building a CI Platform on Github Actions"
44
summary: An introduction to how we run CI at my company for 25 Rails apps on Github Actions using Docker Compose.
5-
cover-img: /assets/img/thumbnails/delayed_job.jpg
6-
thumbnail-img: /assets/img/thumbnails/delayed_job.jpg
7-
share-img: /assets/img/thumbnails/delayed_job.jpg
5+
cover-img: /assets/img/thumbnails/ci-overview.jpg
6+
thumbnail-img: /assets/img/thumbnails/ci-overview.jpg
7+
share-img: /assets/img/thumbnails/ci-overview.jpg
88
readtime: true
99
toc: true
1010
tags:
@@ -14,7 +14,7 @@ tags:
1414
- github-actions
1515
---
1616

17-
# Deploying and providing CI for 25 Rails Apps
17+
# Providing CI for 25 Rails Apps
1818

1919
{: .box-warning .ignore-blockquote }
2020

@@ -24,28 +24,31 @@ tags:
2424
2525
## Brief Background
2626

27-
I work on something similar to a "platform" team for 130 Ruby on Rails developers. We run and provide CI (via self-hosted Github Actions) for the applications that other developers build.
27+
I work on something similar to a "platform" team for 130 Ruby on Rails developers. We run and provide continuous integration (CI) via self-hosted Github Actions for the applications that other developers build.
2828

2929
{: .box-note .ignore-blockquote }
3030

3131
<!-- prettier-ignore -->
3232
>**A bit more background**\\
33-
> To go a bit more in depth all the compute is handled by several Kubernetes clusters and the actual code lives in multiple repositories all under the same organization. Thus, a lot of the challenges we face are around providing flexibility for our developers without implementation details leaking out to them.
33+
> All the compute is handled by several Kubernetes clusters while the actual code lives in multiple repositories all under the same organization. Thus, a lot of the challenges we face are around providing flexibility for our developers without implementation details leaking out to them.
3434
3535
Our apps all follow a fairly similar pattern:
3636

37+
- they have their own github repository under our organization
3738
- they have a `Dockerfile`
38-
- get built as an image
39-
- get served via Kubernetes.
39+
- get built as an docker container image (my terminology might be wrong here)
40+
- get served via Kubernetes
4041

4142
However, despite serving everything as containers our developers don't actually develop in containers. So to avoid the issue where production is the first place that our apps see containerized use we run all our tests in containers.
4243

4344
## How Do We Run CI?
4445

45-
Basically all our apps have a structure like so:
46+
All our apps have this general structure:
4647

4748
```plaintext
4849
.
50+
├── app_code
51+
├── ...
4952
├── .github
5053
│ └── workflows
5154
│ └── main.yaml
@@ -56,9 +59,9 @@ Basically all our apps have a structure like so:
5659
└── docker-compose.test.yaml
5760
```
5861

59-
The `Dockerfile` is nothing worth mentioning, it just sets up our application with whatever dependencies it needs (gems, node modules, etc).
62+
Our `Dockerfile` largely follows [the default generated by Rails](https://github.com/dylhack/rails/blob/main/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt). The difference are mainly in the certs that we add to the image.
6063

61-
The scripts in `bin` as just basic bash scripts that represent the testing process, linting process or whatever. For example:
64+
The scripts in `/bin` as just basic bash scripts that represent the testing process, linting process or whatever. For example:
6265

6366
```bash
6467
# bin/ci-lint
@@ -67,7 +70,7 @@ bundle exec brakeman
6770
bundle exec rubocop
6871
```
6972

70-
The idea is that these are the same scripts that a developer could run locally on their machine as in CI.
73+
The idea is that these are the same scripts that a developer could run locally on their machine. Reusing the scripts ensure they are the source of truth for both local and CI processes.
7174

7275
The `docker-compose` looks something like this:
7376

@@ -93,13 +96,14 @@ services:
9396
retries: 5
9497
```
9598
96-
Running `docker compose up` locally builds the image, starts Postgres, then runs the `command` in a container.
99+
Running `docker compose up` locally builds the image, starts Postgres, then runs `bin/ci-some-command` in against the container with the app. We specify a default command but docker compose allows us to override the command when called via command line.
97100

98-
This translates nicely to CI because the developers are able create arbitrary scripts and those scripts will run in CI the same way as locally. For example if a particular app wants to use Cypress they can add that to the existing script or create a new script for it.
101+
CI runs tests via the same docker compose and an commands that a developer would run locally. Lets take a look at that.
99102

100103
## Getting It All Set Up in Github Actions
101104

102105
So how does this all look as an actions workflow?
106+
103107
{% raw %}
104108

105109
```yaml
@@ -139,10 +143,10 @@ jobs:
139143
uses: our_org/actions/ruby-rspec-report@v4
140144
```
141145

142-
Largely it looks something like:
146+
To sum it up:
143147

144-
1. we pull down the repo
145-
2. set up docker
148+
1. we pull down the repo (checkout)
149+
2. set up docker (login, setup buildx, bake)
146150
3. run the test in docker compose
147151
4. print a report.
148152

@@ -159,33 +163,29 @@ Largely it looks something like:
159163

160164
{% endraw %}
161165

162-
The `DOCKER_RUN_CMD` here is simply to all passing in commands to run other `bin` scripts (ex: `bin/ci-lint` and `bin/ci-rspec`). I omitted showing the potential inputs that this action might receive.
166+
As mentioned before, the command in the `docker-compose` file can be override. The `DOCKER_RUN_CMD` here is passing in commands to run other `bin` scripts (ex: `bin/ci-lint` and `bin/ci-rspec`). I omitted showing the potential inputs that this action might receive.
163167

164168
## Benefits and Drawbacks
165169

166170
### Benefits
167171

168-
The biggest benefit of this approach (and the one that makes it pretty much mandatory) is that our applications see containerized usage before landing in some higher environment (staging, qa, etc). The majority of our developers simply don't develop in docker, this, given that the final product is served in a container, testing in docker is the next best thing.
172+
**Shifting Docker Left**: The majority of our developers don't develop in docker. Thus, for our apps to see any containerized usage before the apps hit staging or production we need to run tests in docker. This makes docker in CI a necessity.
169173

170-
Since our testing happens by starting docker compose and running a bash script with test commands the entire process is very platform agnostic. We don't have to worry about how do you set up ruby on GitHub Actions vs Jenkins vs CircleCI, then repeat for node, etc. We set up docker and that's it.
174+
**Platform Agnosticism**: Since our testing happens by starting `docker compose` and running a script with test commands the entire process is platform agnostic. We don't have to worry about differences in ruby / node / etc on GitHub Actions vs Jenkins vs CircleCI. We set up docker and that's it. And if we are protected if we ever need to switch to another provider.
171175

172176
### Drawbacks
173177

174-
The main drawback (and I mean pure drawback not tradeoff) is performance overhead. This comes in two forms: container overhead and time to build the container.
175-
176-
In terms of the container overhead, running software directly in the runner will always be faster than running it with docker compose on the runner. Does it really matter? Not really.
177-
178-
The real problem we have encountered is the slow speed in building the images. The P90 of the setup step is 15 minutes on our biggest app (when all the layers miss the cache). Fixing this is still a work in progress.
178+
**Performance Overhead**: In terms of the container overhead, running software directly in the runner will always be faster than running it with `docker compose` on the runner. Does it really matter? Not really.
179179

180-
### Shifted Complexity
181-
182-
We have moved the complexity around within the actions jobs. Without docker there might be complexity in ensuring all required packages are present to test the app. With docker you get that for free but instead you have to worry about setting up and building the image. This made sense for us.
180+
**Container Build Overhead**: Building images is slow. The P90 of the setup step is 15 minutes on our biggest app (when all the layers miss the cache). Fixing this is still a work in progress.
183181

184182
## Conclusion
185183

184+
We have moved the complexity around within the actions jobs. Without docker there might be complexity in ensuring all required packages are present to test the app. With docker you get that for free but instead you have to worry about setting up and building the image. This made sense for us, it might not for you.
185+
186186
I left a lot of the detail out of my summary but we have about 25 web apps under us of varying sizes that we support this way. Beyond the initial setup the files in each app repository stay fairly static and developers largely get to ignore them. CI usually just works.
187187

188-
That was a super brief overview of our CI and in a future post I will go a bit more in depth on how we provide a few cool features namely:
188+
That was a super brief overview of our CI and in future posts I will go a bit more in depth on how we provide a few cool features namely:
189189

190190
- variably size parallelization of runs via input: `runner-count: 8`
191191
- providing arbitrary commands to get run via CI: `[{"cmd": "some-command"}]`

assets/img/thumbnails/ci-overview.jpg

193 KB
Loading

0 commit comments

Comments
 (0)