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이 준비 될 때까지 기다리도록 컨테이너를 변경할 수 있다면 그렇게하십시오.

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

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

이 예에서 myserver 는 mydb 컨테이너 의 포트 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.