Docker-compose periksa apakah koneksi mysql sudah siap

92

Saya mencoba memastikan bahwa penampung aplikasi saya tidak menjalankan migrasi / mulai sampai penampung db dimulai dan SIAP menerima koneksi.

Jadi saya memutuskan untuk menggunakan healthcheck dan bergantung pada opsi di file docker compose v2.

Di aplikasi, saya memiliki yang berikut ini

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

Sebaliknya, db memiliki pemeriksaan kesehatan berikut

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

Saya telah mencoba beberapa pendekatan seperti:

  1. memastikan db DIR dibuat test: ["CMD", "test -f var/lib/mysql/db"]
  2. Mendapatkan versi mysql: test: ["CMD", "echo 'SELECT version();'| mysql"]
  3. Ping admin (tandai penampung db sebagai sehat tetapi tampaknya bukan tes yang valid) test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]

Apakah ada yang punya solusi untuk ini?

John Kariuki
sumber
Anda membuat buruh pelabuhan untuk DB? Tolong beritahu saya bahwa data Anda berada di luar penampung ini demi kesehatan aplikasi Anda
Jorge Campos
Atau setidaknya ini adalah wadah uji.
Jorge Campos
Ini hanya untuk tujuan pengembangan / pengujian HANYA sebenarnya.
John Kariuki
2
Saya pikir Anda harus menggunakan perintah untuk menghubungkan dan menjalankan kueri di mysql, tidak ada sampel yang Anda berikan melakukan ini: sesuatu seperti:mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
Jorge Campos
1
@JorgeOfficial Oke terima kasih. Biasanya saya memiliki wadah db, tetapi volume peta untuk dir data. Sehingga jika penampungnya turun, data akan tetap ada ke instansiasi berikutnya.
S ..

Jawaban:

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

Wadah api tidak akan mulai sampai wadah db sehat (pada dasarnya sampai mysqladmin aktif dan menerima koneksi.)

John Kariuki
sumber
12
mysqladmin pingakan mengembalikan positif palsu jika server berjalan tetapi belum menerima koneksi.
halfpastfour.am
53
FYI untuk orang-orang tahun 2017: conditionunder depends_ontidak didukung di versi 3+
Mint
@BobKruithof Saya menghadapi masalah yang sama ... apakah ada solusi, seperti status tidur atau keluar untuk coba lagi
Mukesh Agarwal
1
@dKen lihat jawaban saya di bawah stackoverflow.com/a/45058879/279272 , saya harap ini juga akan berhasil untuk Anda.
Mukesh Agarwal
1
Untuk memeriksanya menggunakan kata sandi: test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]- jika Anda ditentukan MYSQL_ROOT_PASSWORDdi environmentsbagian.
laimison
22

Jika Anda menggunakan docker-compose v3 + , conditionsebagai opsi depends_ontelah dihapus .

Jalur yang dianjurkan adalah dengan menggunakan lebih wait-for-it, dockerizeatau wait-for. Di docker-compose.ymlfile Anda , ubah perintah Anda menjadi:

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

Saya pribadi lebih suka wait-forkarena dapat berjalan dalam wadah Alpine ( shkompatibel, tidak bergantung pada bash). Kekurangannya adalah tergantung pada netcat, jadi jika Anda memutuskan untuk menggunakannya, pastikan Anda telah netcatmenginstalnya di container, atau menginstalnya di Dockerfile Anda, misalnya dengan:

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

Saya juga membuat forkwait-for proyek sehingga dapat memeriksa status HTTP yang sehat (yang digunakannya wget). Kemudian Anda dapat melakukan sesuatu seperti itu:

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

PS: Seorang PR juga siap digabung untuk menambah kapasitas itu ke wait-forproyek.

Capripot
sumber
14

Ini sudah cukup

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
Maksim Kostromin
sumber
2
untuk apa ganda itu $?
InsOp
5
Sintaks khusus @InsOp yang harus Anda gunakan dalam perintah uji kesehatan untuk meng-escape variabel env dimulai dengan $, yaitu $$ MYSQL_PASSWORD akan menghasilkan $ MYSQL_PASSWORD, yang dengan sendirinya akan menghasilkan mypassword dalam contoh konkret ini
Maksim Kostromin
Jadi dengan im mengakses variabel env di dalam wadah? dengan satu $Im mengakses variabel env dari host maka saya kira? bagus sekali terima kasih!
InsOp
10

Jika Anda dapat mengubah wadah untuk menunggu mysql siap, lakukanlah.

Jika Anda tidak memiliki kontrol wadah tempat Anda ingin menyambungkan database, Anda bisa mencoba menunggu port tertentu.

Untuk tujuan itu, saya menggunakan skrip kecil untuk menunggu port tertentu diekspos oleh penampung lain.

Dalam contoh ini, myserver akan menunggu port 3306 dari container mydb dapat dijangkau.

# 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

Anda dapat menemukan dokumentasi skrip wait-for-it di sini

tidak tidak
sumber
Saya mencoba menggunakan wait-for-it.sh sebelumnya tetapi itu menimpa Dockerfile default, bukan? Bagaimana tampilan entrypoint.sh?
John Kariuki
Titik masuknya tergantung pada gambar Anda. Anda dapat memeriksanya dengan buruh pelabuhan inspect <image id>. Ini harus menunggu layanan tersedia dan memanggil titik masuk Anda.
nono
Apakah itu ok? Apakah kamu mengerti?
bukan pada
Masuk akal. Ya.
John Kariuki
6
Peringatan: MySQL 5.5 (mungkin versi yang lebih baru juga) dapat merespons saat masih memulai.
Blaise
8

Hai untuk healthcheck sederhana menggunakan docker-compose v2.1 , saya menggunakan:

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

Pada dasarnya ini menjalankan mysqlperintah sederhana SHOW DATABASES;menggunakan sebagai contoh pengguna rootdengan kata sandi rootpasswd di database.

Jika perintah berhasil maka db sudah up dan ready jadi jalur healthcheck. Anda dapat menggunakannya intervaluntuk menguji pada interval.

Menghapus bidang lain untuk visibilitas, berikut ini akan terlihat seperti di Anda 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
Sylhare
sumber
1
Peringatan: Dengan "versi 3" dari file tulis, dukungan "kondisi" tidak lagi tersedia. Lihat docs.docker.com/compose/compose-file/#depends_on
BartoszK
1
Anda harus menggunakan fitur perintah bersama dengan skrip wait-for-it.sh . Saya melakukannya dengan cara ini:command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]
BartoszK
@BartoszKI tidak mengerti. Bisakah Anda menambahkan jawaban lengkap dengan detail? Saya menghadapi masalah yang persis sama, tetapi saya tidak dapat membuatnya berhasil.
Thadeu Antonio Ferreira Melo
Pastikan Anda menggunakan v2.1, jika tidak ikuti pedoman baru untuk v3.0 dan yang lebih baru.
Sylhare
1
--execute \"SHOW DATABASES;\"itulah yang membuatnya menunggu saya hingga database tersedia untuk diakses aplikasi
tsuz
6

Saya memodifikasi docker-compose.ymlsesuai contoh berikut dan berhasil.

  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

Dalam kasus saya ../schemaAndSeedDataberisi beberapa skema dan file sql penyemaian data. Design your own check scriptbisa mirip dengan berikut select * from LastSchema.LastDBInsert.

Sementara kode wadah bergantung web adalah

depends_on:
  mysql:
    condition: service_healthy
Mukesh Agarwal
sumber
Ini mungkin berhasil untuk Anda tetapi saya tidak yakin apakah ini didukung atau tidak di semua mesin MySQL.
halfpastfour.am
Saya berbicara tentang mesin database seperti InnoDB, MyISAM dll. Apakah LastSchema.LastDBInsertdefault MySQL atau mesin database tertentu?
halfpastfour.am
Tidak, itu juga bukan default di mysql. Itu hanya sampel. kueri palsu.
Mukesh Agarwal
5
Peringatan: Dengan "versi 3" dari file tulis, dukungan "kondisi" tidak lagi tersedia. Lihat docs.docker.com/compose/compose-file/#depends_on
BartoszK
4

Saya memiliki masalah yang sama, saya membuat skrip bash eksternal untuk tujuan ini (Ini terinspirasi oleh jawaban Maxim). Ganti mysql-container-namedengan nama wadah MySQL Anda dan juga kata sandi / pengguna diperlukan:

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

Di MakeFile saya, saya memanggil skrip ini tepat setelah docker-composepanggilan saya :

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

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

Kemudian saya dapat memanggil perintah lain tanpa kesalahan:

Pengecualian terjadi pada driver: SQLSTATE [HY000] [2006] Server MySQL telah hilang

Contoh keluaran:

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.

Saya telah menghapus health check karena sekarang tidak berguna dengan pendekatan ini.

Gulungan
sumber
3

Menambahkan solusi yang diperbarui untuk pendekatan healthcheck. Cuplikan sederhana:

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

Penjelasan : Karena mysqladmin pingmengembalikan positif palsu (terutama untuk kata sandi yang salah), saya menyimpan output ke variabel sementara, kemudian menggunakan grepuntuk menemukan output yang diharapkan ( mysqld is alive). Jika ditemukan maka akan mengembalikan kode kesalahan 0. Jika tidak ditemukan, saya mencetak seluruh pesan, dan mengembalikan 1 kode kesalahan.

Cuplikan diperpanjang:

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

Penjelasan : Saya menggunakan rahasia buruh pelabuhan daripada variabel env (tapi ini bisa dicapai dengan vars env biasa juga). Penggunaan $$is untuk $tanda literal yang dilucuti saat diteruskan ke container.

Output dari docker inspect --format "{{json .State.Health }}" db | jqberbagai kesempatan:

Semuanya baik-baik saja:

{
  "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 belum menyala (belum):

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

Kata sandi salah:

{
  "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"
    }
  ]
}
Maxim_united
sumber
3

RESTART ON-FAILURE

Sejak v3 condition: service_healthytidak lagi tersedia. Idenya adalah bahwa pengembang harus menerapkan mekanisme pemulihan kerusakan dalam aplikasi itu sendiri. Namun untuk kasus penggunaan sederhana, cara sederhana untuk mengatasi masalah ini adalah menggunakan restartopsi.

Jika status layanan mysql menyebabkan aplikasi exited with code 1Anda, Anda dapat menggunakan salah satu restartopsi kebijakan yang tersedia. misalnya,on-failure

version: "3"

services:

    app:
      ...
      depends_on:
        - db:
      restart: on-failure
Hamid Asghari
sumber