GoLang at ZenMate
@nkhumphreys
29th June 2015
nkhumphreys.co.uk
GDG Berlin GoLang
nkhumphreys.co.uk
Who am I
- API & Backend Lead @ ZenMate
- Self confessed Go fanboy
- Previously an embedded C and Python web developer
nkhumphreys.co.uk
What is ZenMate
- VPN/Proxy provider based in Berlin
- 12 million registered users
- Over 200 servers in 10 locations
- Supporting 8 platforms
- Approx. 16 engineers
nkhumphreys.co.uk
What I will say
- The world that was and why swap to Go
- Tailor made user interfaces
- Lessons learnt along the way
- What's next?
- End goal
nkhumphreys.co.uk
The World that was
- Monolithic Ruby API
- Un-scalable
- Dependencies meant local development was hard
- Deployment hell
kill unicorns
kill rabbits
kill god
nkhumphreys.co.uk
The World that was...
- Responsible for 12 million registered users
- Supported all clients with server/location information
- Managed signups and premium purchases
- Managed infrastructure and DNS
- Made DevOps sad!
nkhumphreys.co.uk
The World that was...
Managing infrastructure looked like this
nkhumphreys.co.uk
The World that was...
When DevOps wanted it to look like this
nkhumphreys.co.uk
Why swap
We decided to build it, to make our DevOps lead look like this:
GOAL =>
nkhumphreys.co.uk
Why swap at all?
- Manageable services
- Easy to deploy
- Easy to use <= very subjective!
nkhumphreys.co.uk
First steps
- Infrastructure management
- DevOps team want to stay in the terminal
- They want to script repetitive tasks
Decisions
- API to manage infrastructure
- CLI to interact with API
nkhumphreys.co.uk
Obvious choice
nkhumphreys.co.uk
Obvious choice
- Deploying binaries is easy (no dependencies to install)
- Single binary to run locally (no dependencies to install)
- Manages entire infrastructure including supplier API interaction e.g. DNS, using stdlib (no dependencies to install)
nkhumphreys.co.uk
Best day of my Career
One of my most productive days was throwing away 1,000 lines of code.
Ken Thompson
nkhumphreys.co.uk
Best day of my Career
Replacing 2,234 lines of Ruby with ~600 lines of GoLang
nkhumphreys.co.uk
Deployment
- Deployed using Fabric
- Very simple python script
- Run using supervisor
- No more killing god
supervisorctl restart <insert_service_name>
- NGINX as a reverse proxy
- Our stack is now super simple!
nkhumphreys.co.uk
Services for more than infrastructure
- Not everyone wants a CLI
- Marketing team would be pretty p*ssed if that was the interface to our news service
nkhumphreys.co.uk
Keeping everyone happy
Marketing also had to use this
nkhumphreys.co.uk
Keeping everyone happy
nkhumphreys.co.uk
Keeping everyone happy
Which made them look like this
nkhumphreys.co.uk
Easy-to-use is subjective
- The CLI and the old interface were not what non-technical staff wanted
- For them, neither were easy to use
- A user interface is like a joke, but some jokes don't need explaining, they are just not funny!
nkhumphreys.co.uk
Tailor made UI
-
GoLang has all the tools we need to build tailor made user interfaces
- Using the net/http library we built an API and CLI for devops
-
Using the net/http library and the html/template library we started to build a service that marketing could use
Gopher to the rescue, again!
nkhumphreys.co.uk
Tailor made UI
A little Bootstrap and JQuery, et voila
nkhumphreys.co.uk
Features that helped
Anonymous structures
// handleGetAdminHomePage renders the single page javascript application for
// the admin interface
func handleGetAdminHomePage(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
log.Println("serving Admin")
context := struct {
News []CompleteNewsItem
AllPlatforms []string
}{
getCompleteNewsItems(),
getSupportedPlatforms(),
}
templates.ExecuteTemplate(w, "list", context)
}
nkhumphreys.co.uk
Features that helped
Template language
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
{{ range $n := .News }}
<div id="news-item-{{ $n.NewsItem.ID }}" class="row">
<div class="row">
<div class="row">
<div class="col-md-6">
<h3>{{ $n.NewsItem.InternalReference }}</h3>
</div>
<div class="col-md-2">
<button data-news-id="{{ $n.NewsItem.ID }}">
Edit
</button>
</div>
nkhumphreys.co.uk
Features that helped
Struct tags for JSON rendering and database column mapping
type NewsItem struct {
ID int `json:"-" db:"id"`
InternalReference string `json:"-" db:"internal_reference"`
Content string `json:"content"`
Locale string `json:"-"`
Premium bool `json:"-" db:"premium"`
PublishAt time.Time `json:"-" db:"publish_at"`
}
nkhumphreys.co.uk
We haven't left the stdlib
- We have have made two different UI's
- Tailor made with the user in mind
- All achieved with the Go stdlib (not entirely true, shhhh)
nkhumphreys.co.uk
We now have two happy users
nkhumphreys.co.uk
Whats next...
- Finish replacing our monolithic API with new GoLang services
- Build unique interfaces that do one job, and do it well!
- Our financial reporting interface will most likely look like this:
- Move to continuous deployment
- Deployment should be zero pain
SendMail(email_addr, auth, from, to, msg)
nkhumphreys.co.uk
Lessons Learned
- The UI should be made with the user in mind
- Easy-to-use is subjective
- GoLang has all the necessary features and tools to make Easy-to-use UI's for everyone
- But it will most likely be more than one UI
- The Go tool suite has helped us write maintainable code
- Thank god for GoLint (sorry about all the attempts to kill him!)
nkhumphreys.co.uk
End Goal
- Every user who interacts with our services will have an interface that works the way they want it to
- The tools will be there to make their lives easier, not harder, it will be easy-to-use
-
They will not fear using the tool
- If a mistake can be easily made, that is our fault, not theirs
nkhumphreys.co.uk
@nkhumphreys
Questions?
nkhumphreys.co.uk
@nkhumphreys
Want some ZenMate goodies?
GoLang at ZenMate
By Nathan Humphreys
GoLang at ZenMate
GoLang at ZenMate - stdlib for tailored interfaces
- 1,821