Docker mysql 컨테이너가 작동하고 mysql이 쿼리를받을 준비가되었는지 어떻게 알 수 있습니까?


84

몇 가지 다른 도커 컨테이너를 배포하고 있는데 mysql이 첫 번째입니다. 데이터베이스가 가동되는 즉시 스크립트를 실행하고 다른 컨테이너를 빌드하고 싶습니다. mysql ( 이 공식 mysql 컨테이너에서 ) 을 설정하는 진입 점 스크립트 가 아직 실행 중일 때 실행하려고했기 때문에 스크립트가 실패했습니다 .

sudo docker run --name mysql -e MYSQL_ROOT_PASSWORD=MY_ROOT_PASS -p 3306:3306 -d mysql
[..] wait for mysql to be ready [..]
mysql -h 127.0.0.1 -P 3306 -u root --password=MY_ROOT_PASS < MY_SQL_SCRIPT.sql

도커 컨테이너 내부에서 끝나는 진입 점 mysql 설정 스크립트의 신호를 기다리는 방법이 있습니까? 배쉬 수면은 차선책처럼 보입니다.

편집 : 이와 같은 bash 스크립트에 갔다. 가장 우아하고 잔인한 힘은 아니지만 매력처럼 작동합니다. 누군가가 유용하다고 생각할 수도 있습니다.

OUTPUT="Can't connect"
while [[ $OUTPUT == *"Can't connect"* ]]
do
    OUTPUT=$(mysql -h $APP_IP -P :$APP_PORT -u yyy --password=xxx <       ./my_script.sql 2>&1)
done

mysql 서비스 상태를 확인하기 위해 bash 루프를 추가 하시겠습니까? 나는 그것이 얻을 수있는만큼 좋다고 생각한다.
fabrizioM 2014-08-26

1
감사합니다 @fabrizioM. 나는이 접근 방식으로 갔다.
haren

이 질문에 대한 답변이 여기있습니다 .

답변:


73

mysql-client 패키지를 설치하고 mysqladmin을 사용하여 대상 서버를 ping 할 수 있습니다. 여러 Docker 컨테이너로 작업 할 때 유용합니다. 수면과 결합하여 간단한 대기 루프를 만듭니다.

while ! mysqladmin ping -h"$DB_HOST" --silent; do
    sleep 1
done

22
이것은 아름다운 것입니다. , 너무 부두 노동자의 상태 검사를 위해 좋은 작동 :docker run --health-cmd='mysqladmin ping --silent' -d mysql
나단 아서

4
단위 컨테이너를 기다릴 건강한 I 스크립트 사용while [ $(docker inspect --format "{{json .State.Health.Status }}" <container-name>) != "\"healthy\"" ]; do printf "."; sleep 1; done
ronkot

5
여기서는 작동하지 않습니다. mysqladmin ping실제로 데이터베이스를 사용할 수 있기 전에 성공합니다. 컨테이너가 여전히 실행중인 것 같습니다. 초기화 .sql스크립트입니다.
cweiske

3
도커 이미지에 mysqladmin을 설치하지 않았지만 wget도 작업을 수행했습니다. while ! wget mysql:3306; do sleep 1 done
engin

localhost에서 시도했지만 작동하지 않으면 "127.0.0.1"을 시도하십시오. "localhost"를 사용하면 mysqladmin이 기본 TCP 포트 3306 대신 소켓을 사용하여 연결을 시도합니다.
Luke

38

이 작은 bash 루프는 mysql이 열릴 때까지 기다리므로 추가 패키지를 설치할 필요가 없습니다.

until nc -z -v -w30 $CFG_MYSQL_HOST 3306
do
  echo "Waiting for database connection..."
  # wait for 5 seconds before check again
  sleep 5
done

2
멋지네요. 나는 그것을 1 개의 라이너로 사용했다 : until nc -z $ CFG_MYSQL_HOST 3306; 수면 1; echo "DB가 나오기를 기다리는 중 ..."; 완료
user2707671

8
포트를 사용할 수 있다고해서 서버가 연결을 수락 할 준비가 된 것은 아닙니다. mysqladmin ping여기서 정답입니다.
Quolonel 질문

33

이것은 다른 답변에 대한 의견에서 다소 언급되었지만 자체 항목이 필요하다고 생각합니다.

먼저 다음과 같은 방식으로 컨테이너를 실행할 수 있습니다.

docker run --name mysql --health-cmd='mysqladmin ping --silent' -d mysql

Dockerfile 에도 동등한 것이 있습니다 .

이 명령을 사용하면 docker psdocker inspect컨테이너의 상태가 표시됩니다. 특히 mysql의 경우이 방법은 container 내부에서mysqladmin 사용할 수 있다는 장점이 있으므로 docker 호스트에 설치할 필요가 없습니다.

그런 다음 bash 스크립트를 반복하여 상태가 정상이 될 때까지 기다릴 수 있습니다. 다음 bash 스크립트는 Dennis에 의해 작성되었습니다 .

function getContainerHealth {
  docker inspect --format "{{.State.Health.Status}}" $1
}

function waitContainer {
  while STATUS=$(getContainerHealth $1); [ $STATUS != "healthy" ]; do 
    if [ $STATUS == "unhealthy" ]; then
      echo "Failed!"
      exit -1
    fi
    printf .
    lf=$'\n'
    sleep 1
  done
  printf "$lf"
}

이제 스크립트에서이 작업을 수행 할 수 있습니다.

waitContainer mysql

컨테이너가 실행될 때까지 스크립트가 대기합니다. 컨테이너가 비정상 상태가되면 스크립트가 종료됩니다. 예를 들어 도커 호스트의 메모리가 부족하여 mysql이 자체적으로 충분히 할당 할 수없는 경우 가능합니다.


4
Docker에만 의존하므로 크로스 플랫폼 문제가 적기 때문에 이것이 최선의 접근 방식이라는 것을 알았습니다. 내가 만난 유일한 것은 이미지 진입 점이 mysqlserver를 두 번 회전한다는 것입니다. 한 번은 맨손으로, 한 번은 초기화되었습니다. mysqladmin ping당신이 원하지 않는 첫 번째 스핀 업도 잡습니다. 제 경우에는 예상되는 스키마로 더미 쿼리를 실행하는 것이 가장 효과적 mysql -u root -e "use your_schema;"이었습니다. 즉, 상태 명령을 .
Max

healthcheck컨테이너에 대한 것이 있으면 작동합니다 . 그러나 .State.Health.Status당신이 없으면 존재하지 않습니다. .State.Status대신에 만족 해야 할 수도 있습니다 . 그러나 그것은 running이 OP의 요구에 너무 이르다고 말합니다 .
Jesse Chisholm

1
@JesseChisholm 응? 무엇을 위해 생각 --health-cmd하십니까?
Andrew Savinykh


12

포트의 문제는 포트가 열려 있을 수 있지만 데이터베이스가 아직 준비되지 않은 경우가 있습니다.

다른 솔루션을 사용하려면 호스트 컴퓨터에 mysql oa mysql 클라이언트 를 설치해야 하지만 실제로는 이미 Docker 컨테이너에 포함되어 있으므로 다음과 같은 것을 사용하는 것이 좋습니다.

while ! docker exec mysql mysqladmin --user=root --password=root --host "127.0.0.1" ping --silent &> /dev/null ; do
    echo "Waiting for database connection..."
    sleep 2
done

6
: 이것은 최선의 최단 옵션을 것 같다, 그러나 단지 나를 위해 작동하지 않는 핑, 아직 내가 명령을 사용하므로 나중에 준비가되어 있지 않았다 mysql -u root -proot -e 'status' &> /dev/null대신mysqladmin ping
VinGarcia

9

mysqladmin ping접근 방식 을 사용하는 것이 항상 신뢰할 수있는 것은 아니라는 것을 발견했습니다 . 특히 새 DB를 가져 오는 경우 더욱 그렇습니다. 이 경우 서버에 ping을 할 수 있어도 사용자 / 권한 테이블이 아직 초기화되고 있으면 연결하지 못할 수 있습니다. 대신 다음과 같은 작업을 수행합니다.

while ! docker exec db-container mysql --user=foo --password=bar -e "SELECT 1" >/dev/null 2>&1; do
    sleep 1
done

지금까지이 방법에 문제가 발생하지 않았습니다. VinGarcia가 mysqladmin ping답변 중 하나에 대한 의견에서 비슷한 것을 제안했습니다 .


5

다음 상태 확인은 모든 mysql 컨테이너에서 작동합니다.

db:
    image: mysql:5.7.16
    healthcheck:
      test: ["CMD-SHELL", 'mysql --database=$$MYSQL_DATABASE --password=$$MYSQL_ROOT_PASSWORD --execute="SELECT count(table_name) > 0 FROM information_schema.tables;" --skip-column-names -B']
      interval: 30s
      timeout: 10s
      retries: 4
    extends:
        file: docker-compose-common-config.yml
        service: common_service

5

그래서 아무도 이것을 게시했는지 확실하지 않습니다. 아무도 가지고 있지 않은 것처럼 보이므로 ... mysqladmin에는 대기 기능을 제공하는 명령이 있으며 연결 테스트를 처리 한 다음 내부적으로 재 시도하고 완료되면 성공을 반환합니다.

sudo docker run --name mysql -e MYSQL_ROOT_PASSWORD=MY_ROOT_PASS -p 3306:3306 -d mysql
mysqladmin ping -h 127.0.0.1 -u root --password=MY_ROOT_PASS --wait=30 && mysql -h 127.0.0.1 -P 3306 -u root --password=MY_ROOT_PASS < MY_SQL_SCRIPT.sql

중요한 부분은 mysqladmin ping -h 127.0.0.1 -u root --password=MY_ROOT_PASS --wait=30 -v(가)와 --wait연결이 성공하고 번호가 재 시도에 시도의 양되는 때까지 기다려야 플래그되고.

이상적으로는 도커 컨테이너 내부에서 해당 명령을 실행하는 것이 좋지만 원래 포스터 명령을 너무 많이 수정하고 싶지는 않았습니다.

초기화를 위해 내 make 파일에서 사용되는 경우

db.initialize: db.wait db.initialize


db.wait:
  docker-compose exec -T db mysqladmin ping -u $(DATABASE_USERNAME) -p$(DATABASE_PASSWORD) --wait=30 --silent

db.initialize:
  docker-compose exec -T db mysql -u $(DATABASE_USERNAME) -p$(DATABASE_PASSWORD) $(DATABASE_NAME) < dev/sql/base_instance.sql

3

Django 컨테이너가 시작된 직후에 mysql 컨테이너를 연결하려고 할 때 동일한 문제가 발생했습니다. vishnubob의 wait-for.it.sh 스크립트를 사용하여 해결했습니다 . 계속하기 전에 IP와 호스트가 준비 될 때까지 기다리는 쉘 스크립트입니다. 다음은 내 응용 프로그램에 사용하는 예입니다.

./wait-for-it.sh \
    -h $(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $MYSQL_CONTAINER_NAME) \
    -p 3306 \
    -t 90

이 스크립트에서 mysql 컨테이너에 포트 3306 (기본 mysql 포트)과 mySQL_CONTAINER_NAME에 대해 docker가 서명 한 호스트에서 최대 90 초 (준비되면 정상적으로 실행 됨)를 기다리도록 요청합니다. 스크립트에는 더 많은 변수가 있지만 mw의 경우이 세 가지로 작업했습니다.


1

mysql 컨테이너를 기다리는 도커 컨테이너가 Python 이미지를 기반으로하는 경우 (예 : Django 애플리케이션의 경우) 아래 코드를 사용할 수 있습니다.

장점은 다음과 같습니다.

  • mysql의 IP와 포트가 준비 될 때까지 기다리는 wait-for-it.sh를 기반으로 하지 않지만 이것이 자동으로 mysql 초기화가 완료되었음을 의미하지는 않습니다.
  • 컨테이너에 있어야하는 mysql 또는 mysqladmin 실행 파일을 기반으로하는 셸 스크립트가 아닙니다. 컨테이너가 Python 이미지를 기반으로하기 때문에 해당 이미지 위에 mysql을 설치해야합니다. 아래 솔루션을 사용하면 컨테이너에 이미있는 순수 파이썬 기술을 사용합니다.

암호:

import time

import pymysql


def database_not_ready_yet(error, checking_interval_seconds):
    print('Database initialization has not yet finished. Retrying over {0} second(s). The encountered error was: {1}.'
          .format(checking_interval_seconds,
                  repr(error)))
    time.sleep(checking_interval_seconds)


def wait_for_database(host, port, db, user, password, checking_interval_seconds):
    """
    Wait until the database is ready to handle connections.

    This is necessary to ensure that the application docker container
    only starts working after the MySQL database container has finished initializing.

    More info: https://docs.docker.com/compose/startup-order/ and https://docs.docker.com/compose/compose-file/#depends_on .
    """
    print('Waiting until the database is ready to handle connections....')
    database_ready = False
    while not database_ready:
        db_connection = None
        try:
            db_connection = pymysql.connect(host=host,
                                            port=port,
                                            db=db,
                                            user=user,
                                            password=password,
                                            charset='utf8mb4',
                                            connect_timeout=5)
            print('Database connection made.')
            db_connection.ping()
            print('Database ping successful.')
            database_ready = True
            print('The database is ready for handling incoming connections.')
        except pymysql.err.OperationalError as err:
            database_not_ready_yet(err, checking_interval_seconds)
        except pymysql.err.MySQLError as err:
            database_not_ready_yet(err, checking_interval_seconds)
        except Exception as err:
            database_not_ready_yet(err, checking_interval_seconds)
        finally:
            if db_connection is not None and db_connection.open:
                db_connection.close()

용법:

  1. 이 코드를 wait-for-mysql-db.py애플리케이션의 소스 코드 안에 있는 Python 파일 ( 예 :)에 추가 합니다.
  2. 다른 파이썬 스크립트 작성 (startup.py위의 코드를 먼저 실행하고 나중에 애플리케이션을 시작하는 예 합니다.
  3. 애플리케이션 컨테이너의 Dockerfile이 애플리케이션의 소스 코드와 함께이 두 Python 스크립트를 Docker 이미지로 압축하는지 확인합니다.
  4. docker-compose 파일에서 다음을 사용하여 애플리케이션 컨테이너를 구성합니다 command: ["python3", "startup.py"]..

이 솔루션은 MySQL 데이터베이스 용으로 만들어졌습니다. 다른 데이터베이스에 대해 약간 조정해야합니다.


1

새로운 접근 방식을 기반으로이 문제에 대한 새로운 솔루션을 개발했습니다. 내가 찾은 모든 접근 방식은 데이터베이스에 연결하거나 컨테이너와 TCP 연결을 설정하려고 시도하는 스크립트에 의존합니다. 전체 세부 정보는 waitdb 저장소에서 찾을 수 있지만 내 해결책은 컨테이너에서 검색된 로그에 의존하는 것입니다. 스크립트는 로그가 연결 준비 메시지를 실행할 때까지 기다립니다 . 스크립트는 컨테이너가 처음으로 시작되는지 여부를 식별 할 수 있습니다. 이 경우 스크립트는 초기 데이터베이스 스크립트가 실행되고 데이터베이스가 다시 시작될 때까지 대기하며 새로운 연결 준비 메시지 다시 기다립니다 . 이 솔루션을 MySQL 5.7 및 MySQL 8.0에서 테스트했습니다.

스크립트 자체 ( wait_db.sh ) :

#!/bin/bash

STRING_CONNECT="mysqld: ready for connections"

findString() {
    ($1 logs -f $4 $5 $6 $7 $8 $9 2>&1 | grep -m $3 "$2" &) | grep -m $3 "$2" > /dev/null
}

echo "Waiting startup..."
findString $1 "$STRING_CONNECT" 1 $2 $3 $4 $5 $6 $7
$1 logs $2 $3 $4 $5 2>&1 | grep -q "Initializing database"
if [ $? -eq 0 ] ; then
    echo "Almost there..."
    findString $1 "$STRING_CONNECT" 2 $2 $3 $4 $5 $6 $7
fi
echo "Server is up!"

스크립트는 Docker Compose 또는 Docker 자체에서 사용할 수 있습니다. 아래 예제가 사용법을 명확하게 해주기를 바랍니다.

예제 01 : Docker Compose와 함께 사용

SERVICE_NAME="mysql" && \
docker-compose up -d $SERVICE_NAME && \
./wait_db.sh docker-compose --no-color $SERVICE_NAME

예제 02 : Docker와 함께 사용

CONTAINER_NAME="wait-db-test" && \
ISO_NOW=$(date -uIs) && \
  docker run --rm --name $CONTAINER_NAME \
    -e MYSQL_ROOT_PASSWORD=$ROOT_PASSWORD \
    -d mysql:5.7 && \
./wait_db.sh docker --since "$ISO_NOW" $CONTAINER_NAME

예제 3 : 전체 예제 (테스트 케이스)

전체 예제는 저장소의 테스트 케이스 에서 찾을 수 있습니다 . 이 테스트 케이스는 새로운 MySQL을 시작하고 더미 데이터베이스를 만들고 모든 것이 시작될 때까지 기다린 다음 모든 것이 잘되는지 확인하기 위해 선택 을 실행 합니다. 그 후 컨테이너를 다시 시작하고 시작될 때까지 기다린 다음 새 선택 을 실행하여 연결 준비가되었는지 확인합니다.


1

Docker-compose 기반 프로젝트에 Adams 솔루션을 통합 한 방법은 다음과 같습니다 .

db-ready.shserver컨테이너 폴더에 제목이 지정된 bash 파일을 만들었습니다 (컨테이너에 복사 된 내용- server).

#!bin/bash

until nc -z -v -w30 $MYSQL_HOST 3306
do
  echo "Waiting a second until the database is receiving connections..."
  # wait for a second before checking again
  sleep 1
done

그런 다음 컨테이너 docker-compose run server sh ./db-ready.sh && docker-compose run server yarn run migrate내에서 migrate작업을 실행할 때 serverDB가 연결을 수락 한다는 것을 확인하기 위해 실행할 수 있습니다 .

bash 파일이 실행하려는 명령과 분리되어 있기 때문에이 방법이 마음에 듭니다. 내가 실행하는 db-ready.sh작업을 사용하여 다른 DB를 쉽게 실행할 수 있습니다 .



0

당신에 ENTRYPOINT스크립트, 당신은 당신이 유효한 MySQL의 연결이 아닌지 확인해야합니다.

이 솔루션은 용기에 MySQL의 클라이언트를 설치할 필요가 없습니다와 함께 컨테이너를 실행하는 동안 않습니다 php:7.0-fpm실행하는 nc것이 아니라 설치했기 때문에, 옵션이 아니었다. 또한 포트가 열려 있는지 확인한다고해서 반드시 서비스가 올바르게 실행되고 노출되는 것은 아닙니다. [더 많이]

따라서이 솔루션에서는 MySQL 컨테이너가 연결할 수 있는지 확인하기 위해 PHP 스크립트를 실행하는 방법을 보여줍니다. 이것이 더 나은 접근 방식이라고 생각하는 이유를 알고 싶다면 여기에서 내 의견을 확인 하십시오. .

파일 entrypoint.sh

#!/bin/bash
cat << EOF > /tmp/wait_for_mysql.php
<?php
\$connected = false;
while(!\$connected) {
    try{
        \$dbh = new pdo( 
            'mysql:host=mysql:3306;dbname=db_name', 'db_user', 'db_pass',
            array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
        );
        \$connected = true;
    }
    catch(PDOException \$ex){
        error_log("Could not connect to MySQL");
        error_log(\$ex->getMessage());
        error_log("Waiting for MySQL Connection.");
        sleep(5);
    }
}
EOF
php /tmp/wait_for_mysql.php
# Rest of entry point bootstrapping

이것을 실행하면 유효한 MySQL 연결이있을 때까지 컨테이너의 부트 스트랩 논리를 본질적으로 차단합니다.


0

다음 코드를 사용합니다.

export COMPOSE_PROJECT_NAME = web;

export IS_DATA_CONTAINER_EXISTS = $ (docker volume ls | grep $ {COMPOSE_PROJECT_NAME} _sqldata);

docker-compose up -d;
docker-compose ps;

export NETWORK_GATEWAY=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}' ${COMPOSE_PROJECT_NAME}_webserver1_con);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.