假设你有两个docker服务/容器。一个必须准备好接受来自另一个的连接。正如你刚才注意到的,我没有说 "一个必须运行",因为 "准备接受连接 "和 "运行 "之间是有区别的。docker compose的depends_on 关键并不总是能解决问题,因为它只是检查容器是否正在运行。如果一个服务依赖于Cassandra、MySQL、RabbitMQ、Elasticsearch等服务,往往会出现这种情况。它们需要时间来接受连接。为了解决这个问题,我们可以使用一个额外的脚本。这个脚本将定期ping这些服务,直到它们准备好接受连接,然后再实际运行主服务。
在这个例子中,我们有一个Go和Cassandra服务。Go依赖于Cassandra。Cassandra接受连接的速度很慢。我们将在Go中使用我们的脚本,强迫它首先等待Cassandra接受连接。一旦Cassandra说它准备好了,我们就会启动Go服务。
结构
├── cassandra
│ └── cassandra.go
├── docker
│ ├── cassandra
│ │ ├── Dockerfile
│ │ ├── init.sh
│ │ └── keyspace.cql
│ ├── docker-compose.yaml
│ └── go
│ ├── Dockerfile
│ └── init.sh
└── main.go
文件
cassandra.go
package cassandra
import (
"time"
"github.com/gocql/gocql"
)
// The `gocql: no response received from cassandra within timeout period` error
// will be prevented by increasing the default timeout value. e.g. 5 sec
type Config struct {
Hosts []string
Port int
ProtoVersion int
Consistency string
Keyspace string
Timeout time.Duration
}
func New(config Config) (*gocql.Session, error) {
cluster := gocql.NewCluster(config.Hosts...)
cluster.Port = config.Port
cluster.ProtoVersion = config.ProtoVersion
cluster.Keyspace = config.Keyspace
cluster.Consistency = gocql.ParseConsistency(config.Consistency)
cluster.Timeout = config.Timeout
return cluster.CreateSession()
}
main.go
package main
import (
"log"
"time"
"github.com/you/auth/cassandra"
)
func main() {
cass, err := cassandra.New(cassandra.Config{
// From docker go to docker cassandra: "172.17.0.1"
// From local go to docker cassandra: "127.0.0.1"
Hosts: []string{"172.17.0.1"},
Port: 9042,
ProtoVersion: 4,
Consistency: "Quorum",
Keyspace: "tetris",
Timeout: time.Second * 5,
})
if err != nil {
log.Fatalln(err)
}
defer cass.Close()
log.Printf("%+v\n", cass)
}
docker-compose.yaml
version: "3.7"
services:
tetris-cassandra:
build: "cassandra"
container_name: "tetris-cassandra"
ports:
- "9042:9042"
environment:
- "MAX_HEAP_SIZE=256M"
- "HEAP_NEWSIZE=128M"
tetris-go:
build:
context: ".."
dockerfile: "docker/go/Dockerfile"
container_name: "tetris-go"
environment:
DB_HOST: "tetris-cassandra"
DB_PORT: "9042"
Dockerfile (cassandra)
FROM cassandra:3.11.9
COPY ./keyspace.cql /keyspace.cql
COPY ./init.sh /init.sh
RUN chmod +x /init.sh
ENTRYPOINT ["./init.sh"]
init.sh (cassandra)
#!/bin/bash
set -eu
echo "Creating keyspace ..."
sleep=1
index=0
limit=60
while :
do
cqlsh < /keyspace.cql && echo "$(date) Created keyspace ..." && break
index=$(( index + 1 ))
echo "$(date) Waiting for keyspace $sleep seconds ($index/$limit) ..."
sleep $sleep
if [ $index -eq $limit ]
then
echo "$(date) Failed to create keyspace, terminating ..."
exit 1
fi
done &
./docker-entrypoint.sh "$@"
keyspace.cql (cassandra)
DROP KEYSPACE IF EXISTS tetris;
CREATE KEYSPACE IF NOT EXISTS tetris
WITH replication = {
'class': 'NetworkTopologyStrategy',
'datacenter1': 1
};
Dockerfile (go)
FROM golang:1.15-alpine3.12 as build
WORKDIR /source
COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/tetris main.go
FROM alpine:3.12
COPY --from=build /source/bin/tetris /bin/tetris
COPY --from=build /source/docker/go/init.sh /init.sh
RUN chmod +x /init.sh
ENTRYPOINT ["./init.sh"]
init.sh (go)
#!/bin/sh
set -eu
echo "$(date) Connecting to database ..."
sleep=1
index=0
limit=60
until [ $index -ge $limit ]
do
nc -z "${DB_HOST}" "${DB_PORT}" && break
index=$(( index + 1 ))
echo "$(date) Waiting for database $sleep seconds ($index/$limit) ..."
sleep $sleep
done
if [ $index -eq $limit ]
then
echo "$(date) Failed to connect to database, terminating ..."
exit 1
fi
echo "$(date) Connected to database ..."
echo "$(date) Allowing database 3 seconds to complete migrations ..."
sleep 3
./bin/tetris
测试
$ docker-compose -f docker/docker-compose.yaml up
tetris-cassandra | Mon 01 Feb 2021 10:02:52 PM UTC Waiting for keyspace 1 seconds (14/60) ...
tetris-go | Mon Feb 1 22:02:52 UTC 2021 Waiting for database 1 seconds (19/60) ...
tetris-go | Mon Feb 1 22:02:53 UTC 2021 Waiting for database 1 seconds (20/60) ...
tetris-cassandra | Connection error: ('Unable to connect to any servers', {'127.0.0.1': error(111, "Tried connecting to [('127.0.0.1', 9042)]. Last error: Connection refused")})
tetris-cassandra | Mon 01 Feb 2021 10:02:53 PM UTC Waiting for keyspace 1 seconds (15/60) ...
tetris-go | Mon Feb 1 22:02:54 UTC 2021 Waiting for database 1 seconds (21/60) ...
tetris-cassandra | INFO [main] 2021-02-01 22:02:54,643 CassandraDaemon.java:650 - Startup complete
tetris-go | Mon Feb 1 22:02:55 UTC 2021 Connected to database ...
tetris-go | Mon Feb 1 22:02:55 UTC 2021 Allowing database 3 seconds to complete migrations ...
tetris-cassandra | INFO 2021-02-01 22:02:55,354 MigrationManager.java:338 - Create new Keyspace: KeyspaceMetadata{name=tetris, replication=ReplicationParams{class=NetworkTopologyStrategy, datacenter1=1}}}
tetris-cassandra | Mon 01 Feb 2021 10:02:55 PM UTC Created keyspace ...
tetris-cassandra | INFO 2021-02-01 22:02:55,940 CassandraRoleManager.java:372 - Created default superuser role 'cassandra'
tetris-go | 2021/02/01 22:02:58 &{cons:4 pageSize:5000 ... cfg:{Hosts:[172.17.0.1] CQLVersion:3.0.0 ProtoVersion:4 Port:9042 Keyspace:tetris Consistency:4 ... isClosed:false}