Golang项目在Docker容器内运行时找不到依赖项。

huangapple go评论89阅读模式
英文:

golang project can't find dependencies when run inside a docker container

问题

我有这个golang沙盒项目:https://github.com/cflynn07/golang-db-gateway-example

当我尝试在golang:1.6.0-alpine中运行gateway/gateway.go时:

~/g/s/g/c/golang-db-gateway-example git:master ❯❯❯ docker-compose up gateway
mysql_server is up-to-date
Starting gateway
Attaching to gateway
gateway | gateway.go:7:2: cannot find package "github.com/go-sql-driver/mysql" in any of:
gateway | 	/usr/local/go/src/github.com/go-sql-driver/mysql (from $GOROOT)
gateway | 	/go/src/github.com/go-sql-driver/mysql (from $GOPATH)
gateway | gateway.go:8:2: cannot find package "github.com/gorilla/mux" in any of:
gateway | 	/usr/local/go/src/github.com/gorilla/mux (from $GOROOT)
gateway | 	/go/src/github.com/gorilla/mux (from $GOPATH)
gateway exited with code 1

为什么构建步骤没有检测到我项目中的依赖项,而是在/example/vendor文件夹中?

当我从主机操作系统运行go run gateway/gateway.go时,命令可以正常工作。

目录结构(挂载到容器中的/example):

~/g/s/g/c/golang-db-gateway-example git:master ❯❯❯ tree -L 3
.
├── README.md
├── client
│   └── client.go
├── docker-compose.yml
├── gateway
│   └── gateway.go
├── glide.lock
├── glide.yaml
├── tmp
└── vendor
    └── github.com
        ├── go-sql-driver
        └── gorilla

相关文件:

docker-compose.yml

mysql:
  container_name: mysql_server
  image: mysql:5.7.11
  environment:
    - MYSQL_ROOT_PASSWORD=root
  ports:
    - 3306
gateway:
  container_name: gateway
  image: golang:1.6.0-alpine
  volumes:
    - ./:/example
  working_dir: /example/gateway
  command: go run gateway.go
  environment:
    - MYSQL_ROOT_PASSWORD=root
    - MYSQL_DATABASE=sandbox
  links:
    - mysql

gateway/gateway.go

package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"net/http"
	"os"
)

var db *sql.DB

func main() {
	r := mux.NewRouter()

	var e error
	db, e = sql.Open(
		"mysql", os.ExpandEnv("root:${MYSQL_SERVER_PASSWORD}@mysql_server:3306/${MYSQL_DATABASE}"))
	fmt.Print("error is", e)

	r.HandleFunc("/todos", getTodos).Methods("GET")

	http.ListenAndServe(":8080", r)
	fmt.Printf("gateway")
}

type todo struct{}

func getTodos(w http.ResponseWriter, r *http.Request) {
	t := new(todo)
	s, _ := json.Marshal(t)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	fmt.Fprint(w, string(s))
}

更新1:
我将容器内的数据卷挂载路径更改为将项目挂载到容器的$GOPATH下:

mysql:
  container_name: mysql_server
  image: mysql:5.7.11
  environment:
    - MYSQL_ROOT_PASSWORD=root
  ports:
    - 3306
gateway:
  container_name: gateway
  image: golang:1.6.0-alpine
  volumes:
    - ./:/go/src/github.com/cflynn07/golang-db-gateway-example
  working_dir: /go/src/github.com/cflynn07/golang-db-gateway-example
  command: go run gateway/gateway.go
  environment:
    - MYSQL_ROOT_PASSWORD=root
    - MYSQL_DATABASE=sandbox
  links:
    - mysql

然而,现在Docker似乎挂起了:

~/g/s/g/c/golang-db-gateway-example git:master ❯❯❯ docker-compose up gateway
mysql_server is up-to-date
Recreating gateway
Attaching to gateway
英文:

I have this golang sandbox project:
https://github.com/cflynn07/golang-db-gateway-example

When I try to run gateway/gateway.go inside a golang:1.6.0-alpine

~/g/s/g/c/golang-db-gateway-example git:master ❯❯❯ docker-compose up gateway
mysql_server is up-to-date
Starting gateway
Attaching to gateway
gateway | gateway.go:7:2: cannot find package "github.com/go-sql-driver/mysql" in any of:
gateway | 	/usr/local/go/src/github.com/go-sql-driver/mysql (from $GOROOT)
gateway | 	/go/src/github.com/go-sql-driver/mysql (from $GOPATH)
gateway | gateway.go:8:2: cannot find package "github.com/gorilla/mux" in any of:
gateway | 	/usr/local/go/src/github.com/gorilla/mux (from $GOROOT)
gateway | 	/go/src/github.com/gorilla/mux (from $GOPATH)
gateway exited with code 1

Why isn't the build step detecting my project's dependencies inside the /example/vendor folder?

When I run go run gateway/gateway.go from my host OS, the command works.

Directory structure (mounted inside container at /example)

~/g/s/g/c/golang-db-gateway-example git:master ❯❯❯ tree -L 3
.
├── README.md
├── client
│   └── client.go
├── docker-compose.yml
├── gateway
│   └── gateway.go
├── glide.lock
├── glide.yaml
├── tmp
└── vendor
    └── github.com
        ├── go-sql-driver
        └── gorilla

<br>

Relevant files:

docker-compose.yml

mysql:
  container_name: mysql_server
  image: mysql:5.7.11
  environment:
    - MYSQL_ROOT_PASSWORD=root
  ports:
    - 3306
gateway:
  container_name: gateway
  image: golang:1.6.0-alpine
  volumes:
    - ./:/example
  working_dir: /example/gateway
  command: go run gateway.go
  environment:
    - MYSQL_ROOT_PASSWORD=root
    - MYSQL_DATABASE=sandbox
  links:
    - mysql

gateway/gateway.go

package main

import (
	&quot;database/sql&quot;
	&quot;encoding/json&quot;
	&quot;fmt&quot;
	_ &quot;github.com/go-sql-driver/mysql&quot;
	&quot;github.com/gorilla/mux&quot;
	&quot;net/http&quot;
	&quot;os&quot;
)

var db *sql.DB

func main() {
	r := mux.NewRouter()

	var e error
	db, e = sql.Open(
		&quot;mysql&quot;, os.ExpandEnv(&quot;root:${MYSQL_SERVER_PASSWORD}@mysql_server:3306/${MYSQL_DATABASE}&quot;))
	fmt.Print(&quot;error is&quot;, e)

	r.HandleFunc(&quot;/todos&quot;, getTodos).Methods(&quot;GET&quot;)

	http.ListenAndServe(&quot;:8080&quot;, r)
	fmt.Printf(&quot;gateway&quot;)
}

type todo struct{}

func getTodos(w http.ResponseWriter, r *http.Request) {
	t := new(todo)
	s, _ := json.Marshal(t)
	w.Header().Set(&quot;Content-Type&quot;, &quot;application/json; charset=UTF-8&quot;)
	fmt.Fprint(w, string(s))
}

<br><br>

Update 1
I changed my data-volume mount path inside the container to mount the project under the containers $GOPATH

mysql:
  container_name: mysql_server
  image: mysql:5.7.11
  environment:
    - MYSQL_ROOT_PASSWORD=root
  ports:
    - 3306
gateway:
  container_name: gateway
  image: golang:1.6.0-alpine
  volumes:
    - ./:/go/src/github.com/cflynn07/golang-db-gateway-example
  working_dir: /go/src/github.com/cflynn07/golang-db-gateway-example
  command: go run gateway/gateway.go
  environment:
    - MYSQL_ROOT_PASSWORD=root
    - MYSQL_DATABASE=sandbox
  links:
    - mysql

However now docker appears to hang:

~/g/s/g/c/golang-db-gateway-example git:master ❯❯❯ docker-compose up gateway                                                         ✱
mysql_server is up-to-date
Recreating gateway
Attaching to gateway

答案1

得分: 2

实际上,你成功地运行了Go服务器。它没有挂起,只是在等待连接。由于一些怪癖,没有输出:它没有尝试连接到数据库,并且日志语句被缓冲。

尝试修改gateway.go的main函数:

func main() {
    log.Println("Starting main...")

    conn := os.ExpandEnv("root:${MYSQL_SERVER_PASSWORD}@mysql_server:3306/${MYSQL_DATABASE}")

    var err error
    db, err = sql.Open("mysql", conn)
    if err != nil {
        log.Fatal(err)
    }

    log.Println("pinging", conn)
    if err := db.Ping(); err != nil {
        log.Fatal(err)
    }

    r := mux.NewRouter()
    r.HandleFunc("/todos", getTodos).Methods("GET")

    listen := ":8080"
    log.Printf("Listening on %s\n", listen)
    log.Fatal(http.ListenAndServe(listen, r))
}

运行这个版本会得到以下结果:

$ docker-compose up gateway
mysql_server is up-to-date
Starting gateway
Attaching to gateway
gateway | 2016/03/15 10:58:05 Starting main...
gateway | 2016/03/15 10:58:05 pinging root:@mysql_server:3306/sandbox
gateway | 2016/03/15 10:58:05 default addr for network 'mysql_server:3306' unknown
gateway | exit status 1
gateway exited with code 1

从这里开始你应该没问题了。注意:

  • docker-compose似乎会缓冲标准输出直到换行符
  • log函数(如log.Print)会添加换行符,而fmt.Print不会
  • sql.Open不会连接到数据库,使用sql.Ping(参见wiki
  • MYSQL_SERVER_PASSWORD缺失
  • mysql连接字符串的网络类型缺失(参见示例
  • 启动mysql服务器
  • 需要创建新的或挂载现有的数据库'sandbox'

希望对你有所帮助。

英文:

Actually you successfully got the Go server running. It wasn't hanging, just waiting for connections. Due to some quirks, there's no output: it didn't attempt to connect to the database, and logging statements were buffered.

Try modifying gateway.go main:

func main() {
	log.Println(&quot;Starting main...&quot;)

	conn := os.ExpandEnv(&quot;root:${MYSQL_SERVER_PASSWORD}@mysql_server:3306/${MYSQL_DATABASE}&quot;)

	var err error
	db, err = sql.Open(&quot;mysql&quot;, conn)
	if err != nil {
		log.Fatal(err)
	}

	log.Println(&quot;pinging&quot;, conn)
	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}

	r := mux.NewRouter()
	r.HandleFunc(&quot;/todos&quot;, getTodos).Methods(&quot;GET&quot;)

	listen := &quot;:8080&quot;
	log.Printf(&quot;Listening on %s\n&quot;, listen)
	log.Fatal(http.ListenAndServe(listen, r))
}

Running this version gives:

$ docker-compose up gateway
mysql_server is up-to-date
Starting gateway
Attaching to gateway
gateway | 2016/03/15 10:58:05 Starting main...
gateway | 2016/03/15 10:58:05 pinging root:@mysql_server:3306/sandbox
gateway | 2016/03/15 10:58:05 default addr for network &#39;mysql_server:3306&#39; unknown
gateway | exit status 1
gateway exited with code 1

You should be good to go from there. Note:

  • docker-compose seems to buffer standard output until newline
  • log functions such as log.Print add newlines, fmt.Print doesn't
  • sql.Open doesn't connect to the database, use sql.Ping (see wiki)
  • MYSQL_SERVER_PASSWORD is missing
  • network type of the mysql connection string is missing (see examples)
  • start mysql server too
  • need to create new or mount existing database 'sandbox'

Hope that helps.

答案2

得分: 1

看起来,在我看来,这里的主要问题是你没有提前构建你的Go程序。看起来你把Go源文件放在Docker容器中,并依赖于go run来构建和运行程序。

我猜你可以这样做?这种方式非常像脚本语言的风格。

然而,我认为最好的方式是提前构建Go应用程序。

(注意,下面的内容是我改编现有的makefile代码,但我并没有真正运行它。)
例如,你可以这样构建它:

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -installsuffix netgo -o ./gateway/gateway ./gateway

然后,假设你不需要容器中的其他东西,你可以使用如下的Dockerfile构建Docker镜像:

FROM scratch
ENTRYPOINT ["/gateway"]
ADD ./gateway/gateway /gateway

这样就得到了一个简单、小巧(大约8MB)的容器,其中包含一个静态链接的可执行文件。

英文:

It looks like, in my opinion, that the primary problem here is that you aren't building your Go program ahead of time. It looks like you've put the Go source files in the Docker container and you're relying on go run to build and then run the program.

I guess you can do that? It's very script language style that way.

However, the way that I think works best is to build the Go application ahead of time.

(Note, for the following I adapted existing makefile code but I didn't actually run this.)
For example, you could build it like this:

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -installsuffix netgo -o ./gateway/gateway ./gateway

Then, assuming that you don't need other stuff in that container, you can build the Docker image with a Dockerfile like:

FROM scratch
ENTRYPOINT [&quot;/gateway&quot;]
ADD ./gateway/gateway /gateway

Resulting in a simple, small (about 8 MB) container with one static linked executable in it.

答案3

得分: 0

首先,需要将${MYSQL_SERVER_PASSWORD}更改为${MYSQL_ROOT_PASSWORD},因为环境变量中只有MYSQL_ROOT_PASSWORD变量。

其次,这里的@mysql_server:3306是错误的,正确的写法是@tcp(mysql_server:3306),以便通过TCP连接到MySQL。

conn := os.ExpandEnv("root:${MYSQL_ROOT_PASSWORD}@tcp(mysql_server:3306)/${MYSQL_DATABASE}")

一切都会正常工作,唯一的问题可能是在数据库初始化之前需要暂停一下,然后再运行主程序。有几种方法可以解决这个问题,以下是一些方法:https://docs.docker.com/compose/startup-order/

英文:

First - need to change ${MYSQL_SERVER_PASSWORD} on ${MYSQL_ROOT_PASSWORD}, because environment only has MYSQL_ROOT_PASSWORD variable.

Second - this is wrong @mysql_server:3306, correct one is @tcp(mysql_server:3306) to be able to connect to MySQL via TCP.

conn := os.ExpandEnv(&quot;root:${MYSQL_ROOT_PASSWORD}@tcp(mysql_server:3306)/${MYSQL_DATABASE}&quot;)

All will work fine, the only problem can be while db initializing need to make pause before run main program. There are sever ways to solve this problem here are few methods https://docs.docker.com/compose/startup-order/

huangapple
  • 本文由 发表于 2016年3月15日 11:31:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/36002205.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定