为什么我的执行Golang可执行文件的docker容器在启动后立即挂起



我目前正在macOS上用Golang开发一个小型应用程序,它在本地运行非常完美。

我用一个从头开始制作的Dockerfile制作了一个docker镜像。

我的问题是,当容器被启动时,它会无限期地挂起,docker不会绑定端口,但我仍然可以进入容器内部。

以下是容器内正在运行的进程:

/go/src/app # ps
PID   USER     TIME  COMMAND
1 root      0:00 ./main
13 root      0:00 sh
23 root      0:00 ps

这是我的码头作文:

version: "3.3"
services:
dns:
build: 
context: .
ports:
- "53:53"

这是我的Dokerfile

FROM golang:alpine

RUN apk add git
WORKDIR /go/src/app
COPY . .
RUN go get -d -v
RUN go build main.go
RUN chmod +x main
EXPOSE 53/udp
EXPOSE 53/tcp
CMD ["./main"]

docker试图启动容器的日志:

Building dns
Step 1/10 : FROM golang:alpine
---> 813e7bfc1890
Step 2/10 : RUN apk add git
---> Using cache
---> b796ecde3d09
Step 3/10 : WORKDIR /go/src/app
---> Using cache
---> cf5226146d6c
Step 4/10 : COPY . .
---> e5fd26e9ade8
Step 5/10 : RUN go get -d -v
---> Running in ac4c1fe7dd41
github.com/gojektech/heimdall (download)
github.com/gojektech/valkyrie (download)
github.com/pkg/errors (download)
github.com/stretchr/testify (download)
github.com/davecgh/go-spew (download)
github.com/pmezard/go-difflib (download)
github.com/stretchr/objx (download)
get "gopkg.in/yaml.v3": found meta tag get.metaImport{Prefix:"gopkg.in/yaml.v3", VCS:"git", RepoRoot:"https://gopkg.in/yaml.v3"} at //gopkg.in/yaml.v3?go-get=1
gopkg.in/yaml.v3 (download)
github.com/miekg/dns (download)
get "golang.org/x/crypto/ed25519": found meta tag get.metaImport{Prefix:"golang.org/x/crypto", VCS:"git", RepoRoot:"https://go.googlesource.com/crypto"} at //golang.org/x/crypto/ed25519?go-get=1
get "golang.org/x/crypto/ed25519": verifying non-authoritative meta tag
golang.org/x/crypto (download)
get "golang.org/x/net/ipv4": found meta tag get.metaImport{Prefix:"golang.org/x/net", VCS:"git", RepoRoot:"https://go.googlesource.com/net"} at //golang.org/x/net/ipv4?go-get=1
get "golang.org/x/net/ipv4": verifying non-authoritative meta tag
golang.org/x/net (download)
get "golang.org/x/sys/unix": found meta tag get.metaImport{Prefix:"golang.org/x/sys", VCS:"git", RepoRoot:"https://go.googlesource.com/sys"} at //golang.org/x/sys/unix?go-get=1
get "golang.org/x/sys/unix": verifying non-authoritative meta tag
golang.org/x/sys (download)
get "golang.org/x/net/ipv6": found meta tag get.metaImport{Prefix:"golang.org/x/net", VCS:"git", RepoRoot:"https://go.googlesource.com/net"} at //golang.org/x/net/ipv6?go-get=1
get "golang.org/x/net/ipv6": verifying non-authoritative meta tag
Removing intermediate container ac4c1fe7dd41
---> b9d7f7dfbd1a
Step 6/10 : RUN CGO_ENABLED=0 go build main.go
---> Running in f1e34c2b4ff5
Removing intermediate container f1e34c2b4ff5
---> 948947d5834f
Step 7/10 : RUN chmod +x main
---> Running in f747d80c1784
Removing intermediate container f747d80c1784
---> 48d77cb64ede
Step 8/10 : EXPOSE 53/udp
---> Running in 154f55021335
Removing intermediate container 154f55021335
---> 43fec258b5b7
Step 9/10 : EXPOSE 53/tcp
---> Running in 793767d87201
Removing intermediate container 793767d87201
---> 5304e6d90c07
Step 10/10 : CMD ["./main"]
---> Running in 0d6644f390d2
Removing intermediate container 0d6644f390d2
---> 7fc32c2c2e27
Successfully built 7fc32c2c2e27
Successfully tagged lighthouse_dns:latest
Recreating lighthouse_dns_1 ... done
Attaching to lighthouse_dns_1

它挂在"附加到lighthouse_dns_;永远

如果我通过以下操作手动从容器启动可执行文件:

docker exec -it <container id> /bin/sh
/go/src/app# ./main

以下是项目结构:

.
└── project
├── main.go
└── vendor
└── services
├── dns.go
└── request.go

这是代码:

main.go(根文件夹(

package main
import (
"flag"
"fmt"
"services"
)
func main() {
dnsPort := flag.Int("Port", 53, "Exposed running port")
flag.Parse()
fmt.Print("Starting server")
dnsService := services.Service{
Port:      *dnsPort,
AccessKey: "hot-dog",
}
dnsService.Launch()
}

dns.go(供应商/服务文件夹(

package services
import (
"log"
"net"
"strconv"
"github.com/miekg/dns"
)
type u struct {
AccessKey string
}
// ServeDNS ...
func (service *u) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
sdk := InternalSDK{}
msg := dns.Msg{}
msg.SetReply(r)
switch r.Question[0].Qtype {
case dns.TypeA:
msg.Authoritative = true
domain := msg.Question[0].Name
records, getDomainsError := sdk.GetDomainRecordsByType(domain, dns.TypeA)
if getDomainsError == nil {
for _, record := range records {
msg.Answer = append(msg.Answer, &dns.A{
Hdr: dns.RR_Header{Name: domain, Rrtype: record.Type, Class: record.Class, Ttl: record.TTL},
A:   net.ParseIP(record.Data),
})
}
} else {
// todo: log error
}
}
w.WriteMsg(&msg)
}
type Service struct {
Port      int
AccessKey string
}
// LaunchDNSService ...
func (service *Service) Launch() {
// make a new response chan
srv := &dns.Server{Addr: ":" + strconv.Itoa(service.Port), Net: "udp"}
srv.Handler = &u{}
if err := srv.ListenAndServe(); err != nil {
log.Fatalf("Failed to set udp listener %sn", err.Error())
}
}

request.go(供应商/服务文件夹(

package services
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/gojektech/heimdall/httpclient"
)
type InternalSDK struct {
Timeout   uint
Host      string
Port      uint32
AccessKey string
}
type DomainRecord struct {
Domain string `json:"domain"`
Type   uint16 `json:"type"`
Class  uint16 `json:"class"`
TTL    uint32 `json:"ttl"`
Data   string `json:"data"`
}
// New ...
// GetDomainInformations ...
func (call *InternalSDK) GetDomainRecordsByType(domain string, entryType uint16) ([]DomainRecord, error) {
// Use the clients GET method to create and execute the request
url := fmt.Sprintf("http://localhost:3000/dns/domain/%s/type/%d", strings.TrimSuffix(domain, "."), entryType)
timeout := 1000 * time.Millisecond
client := httpclient.NewClient(httpclient.WithHTTPTimeout(timeout))
// Use the clients GET method to create and execute the request
headers := http.Header{}
headers.Add("hug", "hh")
res, err := client.Get(url, headers)
if err != nil {
return nil, err
}
// Heimdall returns the standard *http.Response object
body, err := ioutil.ReadAll(res.Body)
var domainRecord []DomainRecord
json.Unmarshal([]byte(string(body)), &domainRecord)
return domainRecord, nil
}

它起作用,一旦我退出容器,它就会杀死可执行的执行(正常行为(

你们知道为什么吗?

我在自己的环境中部署了它,服务器启动并在端口53:上侦听

Removing intermediate container 9ca44a8e9e1c
---> 50ac6085b9d6
Step 10/10 : CMD ["./main"]
---> Running in f031cb3bb632
Removing intermediate container f031cb3bb632
---> 61f8a889d84d
Successfully built 61f8a889d84d
Successfully tagged test-64451146:latest
Recreating 64451146_dns_1 ... done
$ docker run -it --rm --net container:64451146_dns_1 nicolaka/netshoot bash
bash-5.0# ss -lnu
State           Recv-Q          Send-Q                    Local Address:Port                      Peer Address:Port
UNCONN          0               0                            127.0.0.11:45648                          0.0.0.0:*
UNCONN          0               0                                     *:53                                   *:*

我可以用nslookup点击它,挖掘并接收回复。我怀疑你的问题是因为你没有看到Starting server消息,为此你只需要添加一个换行符。否则,只有当容器停止时,缓冲区才会被冲洗:

fmt.Print("Starting servern")

您将看到的另一个可能的错误是对localhost:的网络请求

url := fmt.Sprintf("http://localhost:3000/dns/domain/%s/type/%d", strings.TrimSuffix(domain, "."), entryType)

在容器内部,localhost是容器,而不是运行容器的主机。网络在docker中是按名称命名的,类似于文件系统和pid的名称命名方式。这就是为什么我使用上面的--net container:语法来运行具有相同名称空间的第二个容器并查看侦听端口。因此,您需要将URL更改为可以从容器内部访问的内容,如果这取决于您在哪里运行它,那么我经常从容器外部将其作为变量(CLI arg或environment var(注入,而不是将其硬编码到程序中。

最新更新