Docker-compose mysql 연결이 준비되었는지 확인


91

내 앱 컨테이너가 db 컨테이너가 시작되고 연결을 수락 할 준비가 될 때까지 마이그레이션 / 시작을 실행하지 않는지 확인하려고합니다.

그래서 나는 healthcheck를 사용하기로 결정했고 docker compose file v2의 옵션에 따라 달라집니다.

앱에는 다음이 있습니다.

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

반면에 db에는 다음과 같은 상태 검사가 있습니다.

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

다음과 같은 몇 가지 접근 방식을 시도했습니다.

  1. db DIR이 생성되었는지 확인 test: ["CMD", "test -f var/lib/mysql/db"]
  2. mysql 버전 얻기 : test: ["CMD", "echo 'SELECT version();'| mysql"]
  3. 관리자 핑 (db 컨테이너를 정상으로 표시하지만 유효한 테스트가 아닌 것 같음) test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]

누구든지 이것에 대한 해결책이 있습니까?


DB에 대한 도커를 만들었습니까? 데이터가 응용 프로그램의 건강을 위해 외부 컨테이너의 것을 알려주세요
호르헤 캄포스

아니면 적어도 이것은 테스트 컨테이너입니다.
Jorge Campos

이것은 실제로 개발 / 테스트 목적으로 만 사용됩니다.
John Kariuki

2
명령을 사용하여 mysql에서 쿼리를 연결하고 실행해야한다고 생각합니다. 제공 한 샘플 중 어떤 것도 다음과 같은 작업을 수행하지 않습니다.mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
Jorge Campos

1
@JorgeCampos 좋아요 감사합니다. 일반적으로 db 컨테이너가 있지만 데이터 디렉토리에 대한 볼륨을 매핑합니다. 따라서 컨테이너가 다운되면 데이터가 다음 인스턴스화까지 지속됩니다.
S ..

답변:


80
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 컨테이너는 db 컨테이너가 정상이 될 때까지 시작되지 않습니다 (기본적으로 mysqladmin이 실행되고 연결을 수락 할 때까지).


12
mysqladmin ping서버가 실행 중이지만 아직 연결을 수락하지 않으면 거짓 긍정을 반환합니다.
halfpastfour.am

53
그냥 참고로 2017 명에 : condition아래는 depends_on3+ 버전에서 지원되지 않습니다
민트

저도 같은 문제에 직면하고있다 @BobKruithof ... 어떤 작업 주위 후에 다시 수면 또는 종료 상태 같은 것이 있습니다
무 케시 아가 왈

1
@dKen 아래에서 내 대답을 참조하십시오. stackoverflow.com/a/45058879/279272 , 나는 당신에게도 효과가 있기를 바랍니다.
Mukesh Agarwal

1
암호를 사용하여이를 확인하려면 : test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]- 섹션 MYSQL_ROOT_PASSWORD에서 정의한 경우 environments.
laimison 19

22

docker-compose v3 +condition사용하는 경우의 옵션 depends_on제거되었습니다 .

권장되는 경로가 아니라 사용하는 것입니다 wait-for-it, dockerize또는 wait-for. 당신에 docker-compose.yml파일로 명령을 변경 :

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

개인적 wait-for으로 Alpine 컨테이너에서 실행할 수 있기 때문에 선호합니다 ( sh호환성, 의존성 없음 bash). 단점은에 따라 다르 netcat므로 사용하기로 결정한 경우 netcat컨테이너에 설치 했는지 확인 하거나 다음과 같이 Dockerfile에 설치하십시오.

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

또한 건강한 HTTP 상태를 확인할 수 있도록 프로젝트를 분기wait-for 했습니다 (사용 wget). 그런 다음 다음과 같이 할 수 있습니다.

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

추신 : PR은 또한 해당 용량을 wait-for프로젝트 에 추가하기 위해 병합 할 준비 가되었습니다 .


13

이 정도면 충분합니다

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
더블 $은 무엇입니까?
InsOp

5
@InsOp 특수 구문 당신은, 즉 $$ MYSQL_PASSWORD 자체가이 구체적인 예에 mypassword에 발생합니다 $ MYSQL_PASSWORD으로 발생합니다 ENV 변수를 $로 시작 탈출을위한 건강 검진 테스트 명령에 사용해야합니다
막심 Kostromin

그래서이 im이 컨테이너 내부의 env 변수에 액세스합니까? 하나의 $Im이 호스트에서 env 변수에 액세스하면 나는 가정합니까? 감사합니다!
InsOp

10

mysql이 준비 될 때까지 기다리도록 컨테이너를 변경할 수 있다면 그렇게하십시오.

데이터베이스를 연결하려는 컨테이너를 제어 할 수없는 경우 특정 포트를 기다릴 수 있습니다.

이를 위해 다른 컨테이너에 의해 노출 된 특정 포트를 기다리는 작은 스크립트를 사용하고 있습니다.

이 예에서 myservermydb 컨테이너 의 포트 3306 에 도달 할 수 있을 때까지 기다립니다 .

# 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

여기 에서 스크립트 wait-for-it 문서를 찾을 수 있습니다.


wait-for-it.sh 이전에 사용해 보았지만 기본 Dockerfile을 재정의합니까? entrypoint.sh는 어떻게 생겼습니까?
존 카리 우키

진입 점은 이미지에 따라 다릅니다. docker inspect <image id>로 확인할 수 있습니다. 이것은 서비스를 사용할 수있을 때까지 기다렸다가 진입 점을 호출해야합니다.
아니요 아니요

괜찮습니까? 이해 했습니까?
아니요 아니요

말이 되세요. 네.
John Kariuki 17.05.05

6
경고 : MySQL 5.5 (최신 버전도 가능)는 초기화하는 동안 응답 할 수 있습니다.
Blaise

8

docker-compose v2.1 사용하여 간단한 상태 확인을 위해 안녕하세요 .

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

기본적으로 데이터베이스 에서 암호 를 가진 사용자 를 예로 사용하여 간단한 mysql명령 SHOW DATABASES;을 실행 합니다.rootrootpasswd

명령이 성공하면 db가 가동되어 상태 확인 경로가 준비됩니다. interval간격으로 테스트하도록 사용할 수 있습니다 .

가시성을 위해 다른 필드를 제거하면 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
경고 : compose 파일의 "버전 3"에서는 "조건"지원을 더 이상 사용할 수 없습니다. 참조 docs.docker.com/compose/compose-file/#depends_on
BartoszK

1
wait-for-it.sh 스크립트 와 함께 명령 기능을 사용해야합니다 . 이 방법으로하는 나 :command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]
BartoszK

@BartoszKI는 그것을 이해하지 못합니다. 자세한 내용과 함께 전체 답변을 추가해 주시겠습니까? 나는 똑같은 문제에 직면하고 있지만 그것을 작동시킬 수 없습니다.
Thadeu Antonio Ferreira Melo

v2.1을 사용하고 있는지 확인하고, 그렇지 않으면 v3.0 이상에 대한 새로운 지침을 따르십시오.
Sylhare

1
--execute \"SHOW DATABASES;\"데이터베이스 액세스 응용 프로그램을 사용할 때까지 나를 위해 기다릴 만든 것입니다
tsuz

6

docker-compose.yml다음 예제에 따라 수정했고 작동했습니다.

  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

제 경우 ../schemaAndSeedData에는 여러 스키마 및 데이터 시드 SQL 파일이 포함되어 있습니다. Design your own check script다음과 유사 할 수 있습니다 select * from LastSchema.LastDBInsert.

웹 종속 컨테이너 코드가

depends_on:
  mysql:
    condition: service_healthy

이것은 당신을 위해 작동 할 수 있지만 이것이 모든 MySQL 엔진에서 지원되는지 여부는 확실하지 않습니다.
halfpastfour.am

InnoDB, MyISAM 등과 같은 데이터베이스 엔진에 대해 이야기하고 있습니다. LastSchema.LastDBInsertMySQL 기본 또는 데이터베이스 엔진이 특정입니까?
halfpastfour.am

아니요, mysql에서도 기본값이 아닙니다. 샘플 일뿐입니다. 더미 쿼리.
Mukesh Agarwal

5
경고 : compose 파일의 "버전 3"에서는 "조건"지원을 더 이상 사용할 수 없습니다. 참조 docs.docker.com/compose/compose-file/#depends_on
BartoszK

4

나는 똑같은 문제가 있었고이 목적을 위해 외부 bash 스크립트를 만들었습니다 (Maxim 대답에서 영감을 얻었습니다). 교체 mysql-container-name도 비밀번호 / 사용자가 필요 MySQL의 컨테이너의 이름 :

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에서이 스크립트는 docker-compose up call 를 호출합니다.

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

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

그런 다음 오류없이 다른 명령을 호출 할 수 있습니다.

드라이버에서 예외가 발생했습니다. SQLSTATE [HY000] [2006] MySQL 서버가 사라졌습니다.

출력 예 :

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.

이 접근 방식에서는 이제 쓸모가 없으므로 상태 확인을 삭제했습니다.


3

실패시 다시 시작

v3 condition: service_healthy는 더 이상 사용할 수 없기 때문입니다. 아이디어는 개발자가 앱 자체 내에서 충돌 복구를위한 메커니즘을 구현해야한다는 것입니다. 그러나 간단한 사용 사례의 경우이 문제를 해결하는 간단한 방법은 restart옵션 을 사용하는 것입니다.

mysql 서비스 상태로 인해 애플리케이션이 exited with code 1사용 restart가능한 정책 옵션 중 하나를 사용할 수 있습니다. 예 :on-failure

version: "3"

services:

    app:
      ...
      depends_on:
        - db:
      restart: on-failure

2

상태 확인 접근 방식을위한 업데이트 된 솔루션 추가. 간단한 스 니펫 :

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

설명 : mysqladmin ping잘못된 긍정 (특히 잘못된 암호)을 반환 하기 때문에 출력을 임시 변수에 저장 한 다음을 사용 grep하여 예상 출력을 찾습니다 (mysqld is alive ). 발견되면 0 오류 코드를 반환합니다. 찾을 수없는 경우 전체 메시지를 인쇄하고 1 개의 오류 코드를 반환합니다.

확장 된 스 니펫 :

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

설명 : env 변수 대신 docker secrets 를 사용 하고 있습니다 (하지만 일반 env vars에서도 가능합니다). 의 사용은 컨테이너로 전달 될 때 제거되는 $$리터럴 $기호에 사용 됩니다.

docker inspect --format "{{json .State.Health }}" db | jq다양한 경우의 출력 :

괜찮습니다.

{
  "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가 작동하지 않음 (아직) :

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

잘못된 비밀번호:

{
  "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"
    }
  ]
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.