Docker-compose mysql bağlantısının hazır olup olmadığını kontrol edin


92

Uygulama kapsayıcımın, db kapsayıcısı başlatılana ve bağlantıları kabul etmeye HAZIR olana kadar geçişleri çalıştırmadığından / başlamadığından emin olmaya çalışıyorum.

Bu yüzden, sağlık denetimini kullanmaya karar verdim ve docker compose file v2'deki seçeneğe bağlı.

Uygulamada aşağıdakilere sahibim

app:
    ...
    depends_on:
      db:
      condition: service_healthy

Öte yandan db, aşağıdaki sağlık denetimine sahiptir

db:
  ...
  healthcheck:
    test: TEST_GOES_HERE
    timeout: 20s
    retries: 10

Aşağıdakilere benzer birkaç yaklaşım denedim:

  1. db DIR'nin oluşturulduğundan emin olmak test: ["CMD", "test -f var/lib/mysql/db"]
  2. Mysql sürümünü edinme: test: ["CMD", "echo 'SELECT version();'| mysql"]
  3. Yöneticiye ping atma (db kapsayıcısını sağlıklı olarak işaretler ancak geçerli bir test gibi görünmüyor) test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]

Kimsenin buna bir çözümü var mı?


Bir DB için bir docker mı yarattınız? Lütfen bana, uygulamanızın sağlığı açısından verilerinizin bu kabın dışında olduğunu söyleyin
Jorge Campos

Ya da en azından bu bir test konteyneri.
Jorge Campos

Bu, aslında YALNIZCA geliştirme / test amaçlıdır.
John Kariuki

2
Sanırım mysql'de bağlanmak ve bir sorgu çalıştırmak için bir komut kullanmalısınız, verdiğiniz örneklerin hiçbiri bunu yapmıyor:mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
Jorge Campos

1
@JorgeCampos Tamam teşekkürler. Genellikle bir db kapsayıcım var, ancak veri dizini için hacimleri eşleyin. Böylece, konteyner aşağı inerse veriler bir sonraki somutlaştırmaya devam edecek.
S.

Yanıtlar:


81
version: "2.1"
services:
    api:
        build: .
        container_name: api
        ports:
            - "8080:8080"
        depends_on:
            db:
                condition: service_healthy
    db:
        container_name: db
        image: mysql
        ports:
            - "3306"
        environment:
            MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
            MYSQL_USER: "user"
            MYSQL_PASSWORD: "password"
            MYSQL_DATABASE: "database"
        healthcheck:
            test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
            timeout: 20s
            retries: 10

API kapsayıcısı, db kapsayıcısı sağlıklı olana kadar başlamaz (temelde mysqladmin yukarı ve bağlantıları kabul edene kadar).


12
mysqladmin pingsunucu çalışıyorsa ancak henüz bağlantıları kabul etmiyorsa, yanlış bir pozitif döndürür.
halfpastfour.am

53
Bilginize 2017 halkına: conditionaltında depends_on3+ sürümünde desteklenmez
Nane

Aynı sorunla karşı karşıya am @BobKruithof ... herhangi bir iş etrafında, yeniden deneme uyku veya çıkış durumu gibi bir şey
Mukesh Agarwal

1
@dKen cevabımı stackoverflow.com/a/45058879/279272 altında görebilirsiniz , umarım sizin için de işe yarar .
Mukesh Agarwal

1
Bu kullanarak şifreyi kontrol etmek için: test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]- tanımladığınız eğer MYSQL_ROOT_PASSWORDiçinde environmentsbölüm.
laimison

22

Docker-compose v3 + kullanıyorsanız , conditionbir seçenek depends_onolarak kaldırılmıştır .

Tavsiye yol yerine kullanmaktır wait-for-it, dockerizeya wait-for. Senin içinde docker-compose.ymldosyaya, olmasını komutu değiştirin:

command: sh -c 'bin/wait-for db:3306 -- bundle exec rails s'

Ben şahsen tercih ediyorum wait-forçünkü bir Alpine konteynırında çalışabilir ( shuyumlu, bağımlılık yok bash). Dezavantajı, buna bağlı olmasıdır netcat, bu nedenle kullanmaya karar verirseniz netcat, konteynere yüklediğinizden emin olun veya Dockerfile'ınıza kurun, örneğin:

RUN apt-get -q update && apt-get -qy install netcat

Ayrıca , sağlıklı HTTP durumunu kontrol edebilmesi için projeyi çatalladımwait-for (kullanıyor wget). O zaman şöyle bir şey yapabilirsiniz:

command: sh -c 'bin/wait-for http://api/ping -- jest test'

Not: Bir PR da bu kapasiteyi wait-forprojeye eklemek için birleştirilmeye hazır .


14

Bu yeterli olmalı

version: '2.1'
services:
  mysql:
    image: mysql
    ports: ['3306:3306']
    environment:
      MYSQL_USER: myuser
      MYSQL_PASSWORD: mypassword
    healthcheck:
      test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD

2
duble ne $için?
InsOp

5
@InsOp özel sözdizimi, env değişkenlerinden kaçmak için sağlık kontrol testi komutunda kullanmanız gereken $ ile başlar, yani $$ MYSQL_PASSWORD, $ MYSQL_PASSWORD ile sonuçlanacak ve bu somut örnekte kendisi mypassword ile sonuçlanacaktır
Maksim Kostromin

Yani bu im ile konteynerin içindeki env değişkenine erişiliyor mu? $ana bilgisayardan env değişkenine erişen tek bir Im ile o zaman sanırım? bu güzel teşekkür ederim!
InsOp

10

Mysql'nin hazır olmasını beklemek için kabı değiştirebilirseniz yapın.

Veritabanını bağlamak istediğiniz konteynerin kontrolüne sahip değilseniz, belirli bağlantı noktasını beklemeyi deneyebilirsiniz.

Bu amaçla, başka bir konteyner tarafından açığa çıkan belirli bir bağlantı noktasını beklemek için küçük bir komut dosyası kullanıyorum.

Bu örnekte, myserver mydb kapsayıcısının 3306 numaralı bağlantı noktasının ulaşılabilir olmasını bekleyecektir .

# Your database
mydb:
  image: mysql
  ports:
    - "3306:3306"
  volumes:
    - yourDataDir:/var/lib/mysql

# Your server
myserver:
  image: myserver
  ports:
    - "....:...."
  entrypoint: ./wait-for-it.sh mydb:3306 -- ./yourEntryPoint.sh

Komut dosyası bekleme belgelerini burada bulabilirsiniz


Daha wait-for-it.sh önce kullanmayı denedim ama varsayılan Dockerfile'ı geçersiz kılıyor, değil mi? Entrypoint.sh nasıl görünüyor?
John Kariuki

Giriş noktası resminize bağlıdır. Docker inspect <image id> ile kontrol edebilirsiniz. Bu, hizmetin kullanılabilir olmasını beklemeli ve giriş noktanızı aramalıdır.
nono

Tamam mı ? Anlıyor musun?
nono

Mantıklı olmak. Evet.
John Kariuki

6
Uyarı: MySQL 5.5 (muhtemelen daha yeni sürümler de) başlatılırken yanıt verebilir.
Blaise

8

Docker -compose v2.1 kullanarak basit bir sağlık denetimi için merhaba , kullandım:

/usr/bin/mysql --user=root --password=rootpasswd --execute \"SHOW DATABASES;\"

Temel olarak , veritabanında şifresi olan kullanıcıyı örnek olarak kullanarak basit bir mysqlkomut çalıştırır .SHOW DATABASES;rootrootpasswd

Komut başarılı olursa, db çalışır durumda ve böylece sağlık kontrolü yolu hazırdır. intervalAralıklı test etmesi için kullanabilirsiniz .

Görünürlük için diğer alanı kaldırırsanız, işte sizin alanınızda nasıl görüneceği docker-compose.yaml.

version: '2.1'

  services:
    db:
      ... # Other db configuration (image, port, volumes, ...)
      healthcheck:
        test: "/usr/bin/mysql --user=root --password=rootpasswd --execute \"SHOW DATABASES;\""
        interval: 2s
        timeout: 20s
        retries: 10

     app:
       ... # Other app configuration
       depends_on:
         db:
         condition: service_healthy

1
Uyarı: Oluşturma dosyasının "sürüm 3" ile "koşul" desteği artık kullanılamaz. Docs.docker.com/compose/compose-file/#depends_on
BartoszK

1
Wait-for-it.sh betiği ile birlikte komut özelliğini kullanmalısınız . Bunu şu şekilde yapıyorum:command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]
BartoszK

@BartoszKI anlamıyorum. Ayrıntılarla birlikte tam bir cevap ekleyebilir misiniz? Ben de aynı sorunla karşı karşıyayım, ama çalışmasını sağlayamam.
Thadeu Antonio Ferreira Melo

V2.1 kullandığınızdan emin olun, aksi takdirde v3.0 ve üstü için yeni yönergeleri izleyin.
Sylhare

1
--execute \"SHOW DATABASES;\"uygulamanın erişebilmesi için veritabanı hazır olana kadar beni
bekleten şeydi

6

Değiştirilmiş docker-compose.ymlAşağıdaki örnekte uygun olarak ve işe yaradı.

  mysql:
    image: mysql:5.6
    ports:
      - "3306:3306"
    volumes:       
      # Preload files for data
      - ../schemaAndSeedData:/docker-entrypoint-initdb.d
    environment:
      MYSQL_ROOT_PASSWORD: rootPass
      MYSQL_DATABASE: DefaultDB
      MYSQL_USER: usr
      MYSQL_PASSWORD: usr
    healthcheck:
      test:  mysql --user=root --password=rootPass -e 'Design your own check script ' LastSchema

Benim durumumda ../schemaAndSeedDatabirden fazla şema ve veri tohumlama sql dosyaları içeriyor. Design your own check scriptaşağıdakine benzer olabilir select * from LastSchema.LastDBInsert.

Web'e bağlı konteyner kodu

depends_on:
  mysql:
    condition: service_healthy

Bu sizin için işe yarayabilir, ancak bunun tüm MySQL motorlarında desteklenip desteklenmediğinden emin değilim.
halfpastfour.am

InnoDB, MyISAM gibi veritabanı motorlarından bahsediyorum. LastSchema.LastDBInsertMySQL varsayılanı mı yoksa veritabanı motoruna mı özgü?
halfpastfour.am

Hayır, mysql'de de bir varsayılan değildir. Bu sadece bir örnekti. kukla bir sorgu.
Mukesh Agarwal

5
Uyarı: Oluşturma dosyasının "sürüm 3" ile "koşul" desteği artık kullanılamaz. Docs.docker.com/compose/compose-file/#depends_on
BartoszK

4

Durum denetimi yaklaşımı için güncellenmiş bir çözüm ekleme. Basit pasaj:

healthcheck:
  test: out=$$(mysqladmin ping -h localhost -P 3306 -u foo --password=bar 2>&1); echo $$out | grep 'mysqld is alive' || { echo $$out; exit 1; }

Açıklama : mysqladmin pingYanlış pozitifler döndürdüğünden (özellikle yanlış parola için), çıktıyı geçici bir değişkene kaydediyorum, ardından grepbeklenen çıktıyı bulmak için kullanıyorum ( mysqld is alive). Bulunursa 0 hata kodunu döndürür. Bulunmaması durumunda, tüm mesajı yazdırıyorum ve 1 hata kodunu döndürüyorum.

Genişletilmiş snippet:

version: "3.8"
services:
  db:
    image: linuxserver/mariadb
    environment:
      - FILE__MYSQL_ROOT_PASSWORD=/run/secrets/mysql_root_password
      - FILE__MYSQL_PASSWORD=/run/secrets/mysql_password
    secrets:
      - mysql_root_password
      - mysql_password
    healthcheck:
      test: out=$$(mysqladmin ping -h localhost -P 3306 -u root --password=$$(cat $${FILE__MYSQL_ROOT_PASSWORD}) 2>&1); echo $$out | grep 'mysqld is alive' || { echo $$out; exit 1; }

secrets:
  mysql_root_password:
    file: ${SECRETSDIR}/mysql_root_password
  mysql_password:
    file: ${SECRETSDIR}/mysql_password

Explanation : env değişkenleri yerine docker sırlarını kullanıyorum (ancak bu, normal env değişkenleriyle de sağlanabilir). Kullanımı , kaba aktarıldığında sıyrılan $$gerçek $işaret içindir.

Dan Çıktı docker inspect --format "{{json .State.Health }}" db | jqçeşitli vesilelerle:

Herşey yolunda:

{
  "Status": "healthy",
  "FailingStreak": 0,
  "Log": [
    {
    {
      "Start": "2020-07-20T01:03:02.326287492+03:00",
      "End": "2020-07-20T01:03:02.915911035+03:00",
      "ExitCode": 0,
      "Output": "mysqld is alive\n"
    }
  ]
}

DB henüz kalkmadı (henüz):

{
  "Status": "starting",
  "FailingStreak": 1,
  "Log": [
    {
      "Start": "2020-07-20T01:02:58.816483336+03:00",
      "End": "2020-07-20T01:02:59.401765146+03:00",
      "ExitCode": 1,
      "Output": "\u0007mysqladmin: connect to server at 'localhost' failed error: 'Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2 \"No such file or directory\")' Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists!\n"
    }
  ]
}

Yanlış şifre:

{
  "Status": "unhealthy",
  "FailingStreak": 13,
  "Log": [
    {
      "Start": "2020-07-20T00:56:34.303714097+03:00",
      "End": "2020-07-20T00:56:34.845972979+03:00",
      "ExitCode": 1,
      "Output": "\u0007mysqladmin: connect to server at 'localhost' failed error: 'Access denied for user 'root'@'localhost' (using password: YES)'\n"
    }
  ]
}

4

Aynı problemi yaşadım, bu amaçla harici bir bash betiği oluşturdum (Maxim cevabından esinlenilmiştir). mysql-container-nameMySQL kapsayıcınızın adıyla değiştirin ve ayrıca şifre / kullanıcı gereklidir:

bin / wait-for-mysql.sh :

#!/bin/sh
until docker container exec -it mysql-container-name mysqladmin ping -P 3306 -proot | grep "mysqld is alive" ; do
  >&2 echo "MySQL is unavailable - waiting for it... 😴"
  sleep 1
done

MakeFile'ımda, bu komut dosyasını docker-composeyukarı görüşmemden hemen sonra çağırıyorum:

wait-for-mysql: ## Wait for MySQL to be ready
    bin/wait-for-mysql.sh

run: up wait-for-mysql reload serve ## Start everything...

O zaman hata almadan diğer komutları çağırabilirim:

Sürücüde bir istisna oluştu: SQLSTATE [HY000] [2006] MySQL sunucusu ortadan kalktı

Çıktı örneği:

docker-compose -f docker-compose.yaml up -d
Creating network "strangebuzzcom_default" with the default driver
Creating sb-elasticsearch ... done
Creating sb-redis              ... done
Creating sb-db                 ... done
Creating sb-app                ... done
Creating sb-kibana             ... done
Creating sb-elasticsearch-head ... done
Creating sb-adminer            ... done
bin/wait-for-mysql.sh
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
MySQL is unavailable - waiting for it... 😴
mysqld is alive
php bin/console doctrine:cache:clear-metadata
// Clearing all Metadata cache entries
[OK] Successfully deleted cache entries.

Bu yaklaşımla artık faydasız olduğu için sağlık kontrolünü sildim.


3

ARIZA DURUMUNDA YENİDEN BAŞLAT

V3 condition: service_healthyartık mevcut olmadığından. Buradaki fikir, geliştiricinin uygulamanın kendi içinde çökme kurtarma için mekanizma uygulaması gerektiğidir. Ancak basit kullanım durumları için bu sorunu çözmenin basit bir yolu restartseçeneği kullanmaktır .

MySQL hizmet durumu başvurunuzu neden olursa etmek exited with code 1size birini kullanabilirsiniz restartpolitik alternatifleri. Örneğin,on-failure

version: "3"

services:

    app:
      ...
      depends_on:
        - db:
      restart: on-failure
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.