A couple of months ago I started to do some programming in Go and since then I’ve learned a few things on how to distribute the finished code. I have no idea if this is similar to the Debian way of doing it but this is pretty simple, straightforward and gets the job done for me.
Where I work we mainly use Debian so we wrap all our projects into .deb at some point. Then we use Chef recipes to deploy these packages and projects in our infrastructure.
The Debian Package
First, let me show you what the structure of the finished .deb looks like.
. ├── debian │ ├── changelog │ ├── compat │ ├── control │ └── rules ├── etc │ ├── balancer.toml │ └── sv │ └── balancer │ └── run ├── balancer.go ├── Makefile └── src └── github.com └── BurntSushi └── toml
Here we have a standard
etc to hold the projects config/startup scripts and
src for package imports. Then there is a
Makefile and the main
balancer.go source file.
We do not need any special debhelper commands so the default sequence of commands works for us, as it will use our provided Makefile when invoking
#!/usr/bin/make -f #export DH_VERBOSE = 1 %: dh $@
The other files are standard Debian package files, so consider consulting the Debian Policy Manual if they look unfamiliar to you.
In here we have three main targets that will be used by
dpkg-buildpackage. First out is the
clean target invoked by
build called from
dh_auto_build and finally
install which is from
all: build build: export GOPATH=$(PWD); go build -o balancer balancer.go install: install -D -o 0 -g 0 -m 0755 balancer $(DESTDIR)/usr/sbin/balancer install -D -o 0 -g 0 -m 0755 etc/sv/balancer/run $(DESTDIR)/etc/sv/balancer/run install -D -o 0 -g 0 -m 0644 etc/balancer.toml $(DESTDIR)/etc/balancer.toml clean: rm -rf debian/tmp rm -rf debian/files rm -rf balancer rm -rf debian/balancer rm -rf debian/*.substvars rm -rf debian/*.debhelper.log
I export GOPATH with the working directory so that the src directory with my package imports can be found when building the Go binary.
One thing you might notice and may be wondering about could be the etc/sv/blancer/run line in the
We currently use
runit to handle starting/stopping/reloading/respawning of our Go applications.
No daemon? No fork? Use a spoon.
Did you notice that I didn’t write daemon, but applications? Yep, at first I figured I would just use the Debian
that is really not optimal as I really want my Go programs to respawn directly if they crash. Also, implementing a daemon() call in
Go has also proven to be an not so easy task nor recommended which you can read about here and
Anyways this is why we use
runit and trust it to handle the Go applications we write. Here
is a nice post about runit if you want to know more.
Subtree merge external dependencies
If you have any external dependencies for your project we subtree merge them into the
git remote add toml https://github.com/BurntSushi/toml git merge -s ours --no-commit toml/master git read-tree --prefix=src/github.com/BurntSushi/toml -u toml/master git commit -m "Merge github.com/BurntSushi/toml into src"
Now you should be all set to proceed with
dpkg-buildpackage and push your new package to your local mirror.
In Chef we create a new recipe that will install our new program.
package "balancer" do action :install end
runit and let
runsv monitor our new application we need a link in /etc/service/balancer to
point to the /etc/sv/balancer directory we had in our Makefile earlier. We also make a node attribute so we
can enable or disable the application from our Chef config.
if node['balancer']['enable'] && node['balancer']['enable']!=false link '/etc/service/balancer' do to '/etc/sv/balancer' end else link '/etc/service/balancer' do action :delete end end
To let our application pick up config changes from Chef we need a service resource to be able to manage it. Here I have bind SIGUSER2 to handle reloads and also defined the usual start/stop/restart commands.
service 'balancer' do supports [:start, :restart, :stop, :reload] service_name 'balancer' start_command 'sv start balancer' restart_command 'sv restart balancer' stop_command 'sv stop balancer' reload_command 'sv 2 balancer' action :nothing end
We can then put it in to use from a template whenever that resource changes.
template "/etc/balancer.toml" do source "balancer.toml.erb" owner "root" group "root" mode 0644 notifies :reload, "service[balancer]", :delayed end