无法从api服务连接到docker容器中的db主机到db服务以便使用golang中的goose进行迁移

goose is the migration tool that helps me to run all the *sql files and run up queries within database. I want to automate migrations(create tables and stuff) using this tool within the docker container on my api service. The problem is when docker runs command "goose run" it gets an error -goose run: dial tcp: lookup db on 192.168.63.6:53: no such host.

docker-compose

services:
  db:
    build: ./db
    volumes:
      - ./db/pgdata:/pgdata
    image: postgres
    ports:
      - "5432"
    restart: always
    environment:
      - POSTGRES_USER=user
      - POSTGRES_DB=dbname
      - POSTGRES_PASSWORD=123
      - PGDATA=/pgdata

  api:
    build:
      context: ./api
    restart: always
    volumes:
      - ./api:/go/src/github.com/gitlees/todoapp/api
    ports:
      - "5000:8080"
    links: 
      - "db"

Api Dockerfile

RUN go get -u github.com/pressly/goose/cmd/goose

RUN goose postgres "host=db user=user dbname=dbname sslmode=disable password=123" up

RUN commands are executed during build phase. During this phase there is no container yet.

Commands that are meant to connect to other containers should be defined in CMD or ENTRYPOINT in the Dockerfile.

First of all, let us have a deeper look into a Dockerfile. I have set up a repository for demo purposes for this question, too.

# We use a so called two stage build.
# Basically this means we build our go binary in one image
# which has the go compiler and all the required tools and libraries.
# However, since we do not need those in our production image,
# we copy the binary created into a basic alpine image
# resulting in a much smaller image for production.

# We define our image for the build environment...
FROM golang:1.11-alpine3.8 as build

# ...and copy our project directory tp the according place.
COPY . "/go/src/github.com/mwmahlberg/so-postgres-compose"
# We set our work directory...
WORKDIR /go/src/github.com/mwmahlberg/so-postgres-compose
# ...and add git, which - forever reason, is not included into the golang image.
RUN apk add git

# We set up our dependency management and make sure all dependencies outside
# the standard library are installed.
RUN set -x && \
    go get github.com/golang/dep/cmd/dep && \
    dep ensure -v
# Finally, we build our binary and name it accordingly    
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /apiserver

# Now for the second stage, we use a basic alpine image...
FROM alpine:3.8
# ... update it...
RUN apk --no-cache upgrade
# .. and take the binary from the image we build above.
# Note the location: [/usr]{/bin:/sbin} are reserved for
# the OS's package manager. Binaries added to an OS by
# the administrator which are not part of the OS's package
# mangement system should always go below /usr/local/{bin,sbin}
COPY --from=build /apiserver /usr/local/bin/apiserver
# Last but not least, we tell docker which binary to execute.
ENTRYPOINT ["/usr/local/bin/apiserver"]

The last line should actually do the trick: ENTRYPOINT specifies the command to be executed when a container is started. Arguments are appended to this command. So to add your connection string, you could do the following

api:
  build: .
  restart: always
  command: "host=db user=user dbname=dbname sslmode=disable password=123"
  ports:
    - 8080:8080
  links:
    - db

The last thing you should do is to have a static configuration for a docker image, like you show in your example. You basically set a static connection string, which deprives you of much of the flexibility using containers.

However, imho it is bad practice to use command line flags to configure containers, the first place. A much more flexible way is to use environment variables. For example, in kubernetes you would use a config map to set up environment variables which in turn configure a pod. However, environment variables can be used with docker-compose or docker swarm, too.

Hence, I would change the docker-compose.yml to something like this:

version: '3'
services:
  db:
    volumes:
      - ./db/pgdata:/pgdata
    image: postgres
    ports:
      - "5432"
    restart: always
    environment:
      - POSTGRES_USER=user
      - POSTGRES_DB=dbname
      - POSTGRES_PASSWORD=123
      - PGDATA=/pgdata

  # adminer added for convenience
  adminer:
    image: adminer
    restart: always
    ports:
      - 8081:8080
    depends_on:
      - db

  api:
    build: .
    restart: always
    ports:
      - 8080:8080
    depends_on:
      - db
    environment:
      - POSTGRES_USER=user
      - POSTGRES_DB=dbname
      - POSTGRES_PASSWORD=123
      - POSTGRES_HOST=db
      - POSTGRES_PORT=5432    

and use the environment variables to configure the binary.

I added a simple example how to use environment variables in Go to the repo. Note that projects like https://github.com/spf13/cobra/cobra or https://gopkg.in/alecthomas/kingpin.v2 make it much easier to work with environment variables, as they provide automatic type conversion, the ability to easily set default values and much more.

As for a more in depth reasoning why to use environment variables, you might want to read part 3 of the 12 factor app methodology.

hth