İki servis/konteyneriniz olduğunu varsayın. Biri diğerinden bağlantı kabul etmeye hazır olmalıdır. Fark ettiğiniz gibi, "biri çalışıyor olmalı" demedim çünkü "bağlantıları kabul etmeye hazır" ile "çalışıyor olma" arasında fark var. Docker compose öğesinin depends_on anahtarı sorunu her zaman çözemez çünkü yalnızca konteynerin çalışıp çalışmadığını kontrol eder. Eğer bir servis MySQL, RabbitMQ, Elasticsearch benzeri servislere bağlıysa, genellikle böyle olur. Bunların bağlantıları kabul etmeleri zaman alır. Bu sorunu çözmek için ek bir komut dosyası kullanabiliriz. Bu komut dosyası, ana hizmeti çalıştırmadan önce bağlantıları kabul etmeye hazır olana kadar bu hizmetleri düzenli olarak pingleyecektir.


Bu örnekte bir Go ve MySQL servisimiz var. Go MySQL'e bağlıdır. MySQL bağlantıları kabul etmede yavaştır. Komut dosyamızı, Go servisini başlatmadan önce MySQL'in bağlantıları kabul etmesini beklemeye zorlamak için kullanacağız. MySQL hazır olduğunu söyledikten sonra Go servisini başlatacağız.


Yapı


.
├── cmd
│   └── app
│   └── main.go
├── docker
│   └── dev
│   ├── docker-compose.yaml
│   └── go
│   ├── Dockerfile
│   └── init.sh
├── go.mod
└── go.sum

Dosyalar


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 {}
}

Dockerfile


#
# 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


Aynı komut dosyasını diğer hizmetler için de kullanabilir veya buraya başka hizmetler ekleyebilirsiniz.


#!/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"

Test


10 saniye içinde bağlanılamadı


Aslında burada sonlandırmayı simüle etmek için 1 saniyesini kod içinde 0.001 (1 ms) olarak değiştirdim.


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 saniye içinde başarıyla bağlandı


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 ...