Storage
At the beginning I said that we would use Redis in this app. Redis is great as a simple key-value store which we can use for demonstrating how to communicate with a database within our Docker container setup.
We will add the Redis configuration to our docker-compose
file:
docker-compose.yaml
version: '3'
services:
app:
working_dir: /app
stdin_open: true
image: golang:1.14-alpine
volumes:
- ./:/app
env_file:
- .env
ports:
- ${HTTP_PORT}:${HTTP_PORT}
redis:
command: redis-server --port ${REDIS_PORT}
image: redis:6.0-alpine
env_file:
- .env
ports:
- ${REDIS_PORT}:${REDIS_PORT}
The container is named redis
, and it will simply start redis-server
on the port we define in the .env
file. To the .env
file, add the following variables:
.env
REDIS_PORT=8002
REDIS_HOST=redis
REDIS_HOST
here is set to the same name as the container. This is because within the containerized network infrastructure, Docker aliases the Redis container to have that hostname, so we can use this instead of trying to figure out where on the host machine this container was set up on.
Now we want to do something with the storage within the go
application. To do this, we will need to use a Redis client: go-redis is a perfect one for our needs. We will also need to set up the module system to install these packages (within the container).
Run the following:
$ docker-compose up -d
$ docker-compose exec app go mod init <name-of-your-module>
$ docker-compose exec app go get github.com/go-redis/redis
This will set up your go.mod
module file and install go-redis
.
As for what to do with the actual implementation: how about we just increment a counter, and display this on the original endpoint?
main.go
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"log"
"net/http"
"os"
)
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: os.Getenv("REDIS_HOST") + ":" + os.Getenv("REDIS_PORT"),
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
val, err := rdb.Incr(context.Background(), "counter").Result()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write([]byte(fmt.Sprintf("Hello, world! You are visitor #%d", val)))
})
port := os.Getenv("HTTP_PORT")
log.Printf("listening on port %s\n", port)
panic(http.ListenAndServe(":"+port, nil))
}
Run the app and hit the endpoint. You should see the counter increment in the response!
$ curl http://localhost:8000
Hello, world! You are visitor #1
$ curl http://localhost:8000
Hello, world! You are visitor #2
If a developer is using their host Redis instance, they can configure REDIS_HOST
and REDIS_PORT
to point to said instance instead of using the Docker instance as well, meaning that we have the full flexibility that was originally desired.