Deploying from a pipeline
A health check
A deploy that returns zero has moved files, not necessarily started a service. The last step of a deploy asks the new version a question only it can answer: the health endpoint.
require "net/http"
uri = URI(ARGV[0] || "http://localhost:8000/health")
response = Net::HTTP.get_response(uri)
puts "#{response.code} #{response.body}"
exit(response.code == "200" ? 0 : 1)
The Ruby script exits zero only on a 200. Start a small server with a /health route and run the check against it:
require "webrick"
server = WEBrick::HTTPServer.new(Port: 8000)
server.mount_proc("/health") { |_req, res| res.body = "ok" }
server.start
ruby health_check.rb
200 ok
A wrong path or a server that is not up gives a nonzero exit status, and the pipeline marks the deploy failed. That is the whole idea: the check runs against the thing that was just deployed, not against a file that was copied.
The deploy job
A deploy job goes after the tests and the build, and it waits for a person when the environment is production. In GitHub Actions the gate is an environment with required reviewers:
deploy:
needs: test
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.4"
bundler-cache: true
- run: bundle exec rake ci
- run: bundle exec ruby health_check.rb https://example.com/health
GitLab has the same idea with when: manual on the deploy job and a protected environment, and the health check is the last line of the script. The address comes from a variable, so the same job deploys to staging and production.
Most Ruby deploys copy files over SSH with scp. Those commands — ssh, scp, a container registry login — are the part the pipeline should keep small: one command whose arguments come from variables, so the job shows the address and never the key.
What to watch for
A health check that always answers 200 ok from a
static file proves nothing. Make it touch the dependency that matters:
the database connection, the cache, the background queue. If the service
can be up and useless, the check must ask about the useless part.
Keep a way back. A deploy job that cannot roll back turns a bad release
into a long outage; the previous image tag or the previous package is
enough, as long as it is named in the job.
Next: Ruby tools a pipeline runs
Article author: Arthur Isaev