假设你有两个docker服务/容器。一个必须准备好接受来自另一个的连接。正如你刚才注意到的,我没有说 "一个必须运行",因为 "准备接受连接 "和 "运行 "之间是有区别的。docker compose的depends_on 关键并不总是能解决问题,因为它只是检查容器是否正在运行。如果一个服务依赖于MySQL、RabbitMQ、Elasticsearch这样的服务,通常会出现这种情况。它们需要时间来接受连接。为了解决这个问题,我们可以使用一个额外的脚本。这个脚本将定期ping这些服务,直到它们准备好接受连接,然后再实际运行主服务。
在这个例子中,我们有一个Go和MySQL服务。Go依赖于MySQL。MySQL接受连接的速度很慢。我们将在Go中使用我们的脚本,强迫它首先等待MySQL接受连接。一旦MySQL说它准备好了,我们就会启动Go服务。
结构
.
├── cmd
│ └── app
│ └── main.go
├── docker
│ └── dev
│ ├── docker-compose.yaml
│ └── go
│ ├── Dockerfile
│ └── init.sh
├── go.mod
└── go.sum
文件
main.go
package main
import (
"database/sql"
"log"
_ "github.com/go-sql-driver/mysql"
)
func main() {
log.Println("Staring the app ...")
db, err := sql.Open("mysql", "user:pass@tcp(app-db:3306)/db")
if err != nil {
log.Fatalln(err)
}
defer db.Close()
if err := db.Ping(); err != nil {
log.Fatalln(err)
}
log.Println("Connected to the db ...")
select {}
}
Docker文件
#
# STAGE 1: prepare
#
FROM golang:1.13.1-alpine3.10 as prepare
WORKDIR /source
COPY go.mod .
COPY go.sum .
RUN go mod download
#
# STAGE 2: build
#
FROM prepare AS build
COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/app -v cmd/app/main.go
#
# STAGE 3: run
#
FROM alpine:3.10 as run
COPY --from=build /source/bin/app /app
COPY --from=build /source/docker/dev/go/init.sh /init.sh
RUN chmod +x /init.sh
ENTRYPOINT ["/init.sh"]
init.sh
你可以对其他服务使用完全相同的脚本,或者直接在这里添加其他服务。
#!/bin/sh
set -eu
echo "Checking DB connection ..."
i=0
until [ $i -ge 10 ]
do
nc -z app-db 3306 && break
i=$(( i + 1 ))
echo "$i: Waiting for DB 1 second ..."
sleep 1
done
if [ $i -eq 10 ]
then
echo "DB connection refused, terminating ..."
exit 1
fi
echo "DB is up ..."
/app
docker-compose.yaml
version: "3.4"
services:
app-go:
build:
context: "../.."
dockerfile: "docker/dev/go/Dockerfile"
ports:
- "8001:8000"
app-db:
image: "mysql:5.7.24"
ports:
- "3001:3306"
environment:
MYSQL_ROOT_PASSWORD: "root"
MYSQL_DATABASE: "db"
测试
10秒内连接失败
实际上我在脚本中把1 秒改为0.001 (1ms),以便在这里模拟终止。
Recreating dev_app-go_1 ... done
Starting dev_app-db_1 ... done
Attaching to dev_app-db_1, dev_app-go_1
app-db_1 | 2020-04-17T19:31:26.156962Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated.
app-db_1 | 2020-04-17T19:31:26.158227Z 0 [Note] mysqld (mysqld 5.7.24) starting as process 1 ...
app-db_1 | 2020-04-17T19:31:26.161203Z 0 [Note] InnoDB: PUNCH HOLE support available
app-db_1 | 2020-04-17T19:31:26.161454Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
app-go_1 | Checking DB connection ...
app-go_1 | 1: Waiting for DB 1 second ...
app-db_1 | 2020-04-17T19:31:26.161563Z 0 [Note] InnoDB: Uses event mutexes
app-go_1 | 2: Waiting for DB 1 second ...
app-go_1 | 3: Waiting for DB 1 second ...
app-db_1 | 2020-04-17T19:31:26.161853Z 0 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used
app-go_1 | 4: Waiting for DB 1 second ...
app-db_1 | 2020-04-17T19:31:26.162130Z 0 [Note] InnoDB: Compressed tables use zlib 1.2.11
app-db_1 | 2020-04-17T19:31:26.162368Z 0 [Note] InnoDB: Using Linux native AIO
app-go_1 | 5: Waiting for DB 1 second ...
app-go_1 | 6: Waiting for DB 1 second ...
app-go_1 | 7: Waiting for DB 1 second ...
app-db_1 | 2020-04-17T19:31:26.162986Z 0 [Note] InnoDB: Number of pools: 1
app-go_1 | 8: Waiting for DB 1 second ...
app-db_1 | 2020-04-17T19:31:26.163323Z 0 [Note] InnoDB: Using CPU crc32 instructions
app-go_1 | 9: Waiting for DB 1 second ...
app-db_1 | 2020-04-17T19:31:26.164968Z 0 [Note] InnoDB: Initializing buffer pool, total size = 128M
app-db_1 | 2020-04-17T19:31:26.172335Z 0 [Note] InnoDB: Completed initialization of buffer pool
app-go_1 | 10: Waiting for DB 1 second ...
app-go_1 | DB connection refused, terminating ...
dev_app-go_1 exited with code 1
3秒内成功连接
Recreating dev_app-go_1 ... done
Starting dev_app-db_1 ... done
Attaching to dev_app-db_1, dev_app-go_1
app-db_1 | 2020-04-17T19:31:26.156962Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated.
app-db_1 | 2020-04-17T19:31:26.158227Z 0 [Note] mysqld (mysqld 5.7.24) starting as process 1 ...
app-db_1 | 2020-04-17T19:31:26.161203Z 0 [Note] InnoDB: PUNCH HOLE support available
app-db_1 | 2020-04-17T19:31:26.161454Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
app-go_1 | Checking DB connection ...
app-go_1 | 1: Waiting for DB 1 second ...
app-db_1 | 2020-04-17T19:31:26.161563Z 0 [Note] InnoDB: Uses event mutexes
app-go_1 | 2: Waiting for DB 1 second ...
app-go_1 | 3: Waiting for DB 1 second ...
app-db_1 | 2020-04-17T19:45:21.324028Z 0 [Note] mysqld: ready for connections.
app-db_1 | Version: '5.7.24' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)
app-go_1 | DB is up ...
app-db_1 | 2020-04-17T19:45:21.663343Z 2 [Note] Got an error reading communication packets
app-go_1 | 2020/04/17 19:45:21 Staring the app ...
app-go_1 | 2020/04/17 19:45:21 Connected to the db ...