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 myapp
folder, 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 myapp
andmypackage
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 updev-build:
GO_MOD=go.mod docker-compose up --builddev-local:
GO_MOD=go.local.mod docker-compose updev-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