How I deploy the Plant-Waterify API
Here is how I deploy the Plant-Waterify API to my VPS
This is a very basic deployment process I use to automatically push updates made to master
to production for the API for my Plant Waterify project.
NOTE: You will need to have a Gitlab runner already set up for deployments to actually run on. I may do an article on Gitlab Runners in the future.
What are we deploying
Here is the repo for the API:
https://gitlab.jasondale.me/jdale/plant-waterify-api
More details can be found in my other post linked above, but this is an API built in Go running on a VPS (that also hosts the site you’re reading this on, as well as all my other projects).
Docker
I have a Docker image I created that I use for Hugo deployments. Here is the DockerHub link:
https://hub.docker.com/repository/docker/jasonsdocker2018/hugoserver
If you want to create your own version, you can find a starter Dockerfile here.
Gitlab CI/CD
The gitlab-ci.yml
does the following steps:
- Specify the Docker image
- Configure the stages (just one in this case)
- Specify what branches this deployment runs for (master only)
- Set up SSH keys and known_hosts to authenticate and deploy to the VPS
- Run a
make
command to do the build and actual deployment
Gitlab Setup
First, you will need to create the variables in the project settings in Gitlab. Navigate to the project, Settings -> CI/CD
Then you will need to add a Gitlab variable
for each of the following items we will use later (I have additional for other projects):
- SSH private key (SSH key authentication to your server - likely the contents of your
~/.ssh/id_rsa
file) - SSH known hosts (to prevent the SSH connection from asking if you want to accept the connection during the script)
I was able to get the proper known_hosts file contents using this command:
ssh-keyscan jasondale.me
These variables can be called in the gitlab-ci.yml
file.
Gitlab CI YAML
Here is the actual .gitlab-ci.yml
file:
image: jasonsdocker2018/hugoserver:latest
stages:
- deploy
deploy:
stage: deploy
image: jasonsdocker2018/hugoserver:latest
only:
- master
before_script:
##
## Install ssh-agent if not already installed, it is required by Docker.
## (change apt-get to yum if you use an RPM-based image)
##
- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'
##
## Run ssh-agent (inside the build environment)
##
- eval $(ssh-agent -s)
##
## Create the SSH directory and give it the right permissions
##
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
##
## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
## We're using tr to fix line endings which makes ed25519 keys work
## without extra base64 encoding.
## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556
##
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- echo "$SSH_PRIVATE_KEY"
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
##
## Optionally, if you will be using any Git commands, set the user name and
## and email.
##
# - git config --global user.email "user@example.com"
# - git config --global user.name "User name"
script:
- make deploy
Makefile
The last step is the make deploy
. I use a makefile
to build and deploy - this can be used on a dev machine or, with the above process, via Gitlab.
Here is the makefile
:
PROJECT_NAME := "plant-waterify-api"
PKG := "gitlab.jasondale.me/jdale/$(PROJECT_NAME)"
PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/)
GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go)
REV := $(shell git rev-parse HEAD)
TOKEN := $(shell cat config.yaml | grep RollbarToken | awk '{ print $$2 }')
USER := $(shell whoami)
BRANCH := $(shell git branch | awk '{print $$2}')
SERVER := "jason@jasondale.me"
dep: ## Get the dependencies
@go get -v -d ./...
build: dep ## Build the binary file
@go build -v $(PKG)
build-mac: dep ## Build the binary file for MacOS
@env GOOS=darwin go build -v $(PKG)
deploy: build
ssh $(SERVER) "sudo service plant-waterify-api stop"
rsync plant-waterify-api $(SERVER):/home/jason/www-data/plant-waterify-api/
rsync plant-waterify-api.sh $(SERVER):/home/jason/www-data/plant-waterify-api/
ssh $(SERVER) "sudo service plant-waterify-api start"
curl -H "Content-Type: application/json" -H "X-Rollbar-Access-Token: $(TOKEN)" -X POST -d '{"environment":"production","revision":"$(REV)", "local_username":"$(USER)","comment":"Branch: $(BRANCH)"}' https://api.rollbar.com/api/1/deploy
What this does:
- ssh to the VPS and stop the current running service
- rsync the built file
- rsync the script used to actually start the go app (this is what the
.service
file actually calls) - start the service again
- if we get here, deployment was successful, let Rollbar know via a curl command
The service can be set up using this.
That’s it! Deployment successful!