Golang app in a container with hot reloading and dynamic local package

A good way to develop Go applications is to use a Docker container to run them. In addition, having hot reloading is a useful feature to save time and directly see code updates. Finally, being able to switch easily between local packages and released packages is very important for micro-services development.

In this article, we will see a way to create an app that allows these three features.

Requirements

The requirement to make it work:

  • Docker installed.

How it works

Container and hot reloading

Create a container with hot reloading for a Go application is easy. In a Dockerfile, get this package:

RUN go get github.com/githubnemo/CompileDaemon

Then build and start the application with this package:

ENTRYPOINT CompileDaemon --build="go build main.go" --command="./main"

Dynamic local package

Using a dynamic local package is less straightforward.

Withgo get ...command, the package will be downloaded from an external source. In order to use a local package, the go mod edit -replace ... should be used. For example:

$ go mod edit -replace <path-to-your-repository>/mypackage=~/mypackage

But firstly, it adds local links in the go.mod file that should be removed before pushing the code (where your local doesn’t exist anymore). Secondly, local packages are not directly reachable by containers.

The idea here is to import local packages as volumes and to have two go.mod files to switch between released packages references and local packages references. Then use different commands to run each mode.

More details below.

Create the application

The first steps are to create a package and an application that will call the package.

In a new folder, to start we have this structure:

myapp/
Dockerfile
Makefile
docker-compose.yml
main.go
mypackage/
mytest/
mytest.go

Create a simple package

In a mytest.go, create a function that returns a simple message:

package mytest// GetMyTest just return a string
func GetMyTest() string {
message := "Hello World from Mypackage"
return message
}

Then, in mypackage folder, execute the following command to create the Go module dependencies:

$ go mod init <path-to-your-repository>/mypackage

It should create mypackage/go.mod file.

Create a simple application

In a main.go, import, and call the package in a function that returns a simple message:

package mainimport (
"fmt"
"<path-to-your-repository>/mypackage/mytest"
)
func main() {
message := "Call Package:"
fmt.Println(message, mytest.GetMyTest())
}

Then, in myappfolder, execute the following command to create the Go module dependencies:

$ go mod init <path-to-your-repository>/myapp

It should create myapp/go.mod file.

Dockerize the application

Set the context folder in thedocker-compose.yml:

version: "3"
services:
myapp:
build:
dockerfile: Dockerfile
context: ./

Then in the Dockerfile choose the/go/src/app as WORKDIR and copy the app, download the packages, build and start with hot reloading package

FROM golang:latestWORKDIR /go/src/appCOPY . .RUN go mod download -xRUN go get github.com/githubnemo/CompileDaemonENTRYPOINT CompileDaemon --build="go build main.go" --command="./main"

Create a copy of your go.mod, and add the local package:

$ cp go.mod go.local.mod
$ go mod edit -replace <path-to-your-repository>/mypackage=~/mypackage go.local.mod

Indocker-compose.yml add myappandmypackage as volume, and override thego.mod by ago.local.mod that will contain references to local packages; ${GO_MOD} is a variable that will contain go.mod or go.local.mod:

version: "3"
services:
myapp:
build:
dockerfile: Dockerfile
context: ./
volumes:
- ./:/go/src/app/
- ./${GO_MOD}:/go/src/app/go.mod
- ../mypackage:/go/src/mypackage

Finally, create a Makefile to be able to switch between usages. The GO_MOD environment variable is injected before running the docker-compose up command:

SERVICE := go-hotreloaddev:
GO_MOD=go.mod docker-compose up
dev-build:
GO_MOD=go.mod docker-compose up --build
dev-local:
GO_MOD=go.local.mod docker-compose up
dev-local-build:
GO_MOD=go.local.mod docker-compose up --build

How to use

To edit your app and your local package, execute:

$ make dev-local

To edit your app and use the released package, execute:

$ make dev

Conclusion

A constraint in this solution is to manually add each package as a volume in the docker-compose.yml. But for simple usage, it does the job.

Source code can be found here: https://github.com/phoax/go-hotreload

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store