Kubernetes:使用 MySQL 容器创建 StatefulSet 时出错

Kubernetes: Error when creating a StatefulSet with a MySQL container

早上好,

我是 Docker 和 Kubernetes 的新手,我真的不知道从哪里开始寻求帮助。我使用 Docker 创建了一个数据库容器,我想管理它并使用 Kubernetes 进行扩展。我开始在我的机器上安装 minikube,并尝试先创建一个部署,然后为数据库容器创建一个 StatefulSet。但是在使用数据库(mariadb 或 mysql)创建 Pod 时,StatefulSet 出现问题。当我使用 Deployment 时,Pods 被加载并且工作正常。但是,在 StatefulSet 中使用它们时,相同的 Pods 不起作用,返回请求 MYSQL 常量的错误。这是部署,我使用命令 kubectl create -f deployment.yaml:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
 name: mydb-deployment
spec:
 template:
  metadata:
   labels: 
    name: mydb-pod
  spec:
   containers:
    - name: mydb
      image: ignasiet/aravomysql
      ports:
       - containerPort: 3306

并且在列出部署时:kubectl get Deployments:

NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
mydb-deployment    1         1         1            1           2m

和pods:kubectl get pods

NAME                                READY   STATUS    RESTARTS   AGE
mydb-deployment-59c867c49d-4rslh    1/1     Running   0          50s

但是因为我想创建一个持久数据库,所以我尝试创建一个具有相同容器和持久卷的 statefulSet 对象。 因此,当使用 kubectl create -f statefulset.yaml 创建以下 StatefulSet 时:

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
 name: statefulset-mydb
spec:
 serviceName: mydb-pod
 template:
  metadata:
   labels: 
    name: mydb-pod
  spec:
   containers:
    - name: aravo-database
      image: ignasiet/aravomysql
      ports:
       - containerPort: 3306
      volumeMounts:
       - name: volume-mydb
         mountPath: /var/lib/mysql
   volumes:
    - name: volume-mydb
      persistentVolumeClaim: 
       claimName: config-mydb

使用服务kubectl create -f service-db.yaml

apiVersion: v1
kind: Service
metadata:
 name: mydb
spec:
 type: ClusterIP
 ports:
  - port: 3306
 selector:
  name: mydb-pod

和权限文件kubectl create -f permissions.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: config-mydb
spec:
 accessModes: 
  - ReadWriteOnce
 resources:
  requests:
   storage: 3Gi

pods 不起作用。他们给出一个错误:

NAME                    READY   STATUS             RESTARTS   AGE
statefulset-mydb-0      0/1     CrashLoopBackOff   1          37s

并且在分析日志时 kubectl logs statefulset-mydb-0:

`error: database is uninitialized and password option is not specified
You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD`

当容器已经有初始化脚本并且运行良好时,它怎么可能会请求这些变量?为什么它只在作为 statefulSet 启动时询问,而不是在启动 Deployment 时询问?

提前致谢。

我提取了您的图像 ignasiet/aravomysql 以试图找出问题所在。事实证明,您的图像已经在 /var/lib/mysql:

处有一个初始化的 MySQL 数据目录
$ docker run -it --rm --entrypoint=sh ignasiet/aravomysql:latest
# ls -al /var/lib/mysql 
total 110616
drwxr-xr-x 1 mysql mysql      240 Nov  7 13:19 .
drwxr-xr-x 1 root  root        52 Oct 29 18:19 ..
-rw-rw---- 1 root  root     16384 Oct 29 18:18 aria_log.00000001
-rw-rw---- 1 root  root        52 Oct 29 18:18 aria_log_control
-rw-rw---- 1 root  root      1014 Oct 29 18:18 ib_buffer_pool
-rw-rw---- 1 root  root  50331648 Oct 29 18:18 ib_logfile0
-rw-rw---- 1 root  root  50331648 Oct 29 18:18 ib_logfile1
-rw-rw---- 1 root  root  12582912 Oct 29 18:18 ibdata1
-rw-rw---- 1 root  root         0 Oct 29 18:18 multi-master.info
drwx------ 1 root  root      2696 Nov  7 13:19 mysql
drwx------ 1 root  root        12 Nov  7 13:19 performance_schema
drwx------ 1 root  root        48 Nov  7 13:19 yypy

但是,当将 PersistentVolume 或只是一个简单的 Docker 卷安装到 /var/lib/mysql 时,它最初是空的,因此脚本认为您的数据库未初始化。您可以通过以下方式重现此问题:

$ docker run -it --rm --mount type=tmpfs,destination=/var/lib/mysql ignasiet/aravomysql:latest
error: database is uninitialized and password option is not specified 
  You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD

如果你有一堆脚本需要运行初始化数据库,你有两个选择:

  1. 根据mysqlDocker文件创建一个Docker文件,并将shell脚本或SQL脚本添加到/docker-entrypoint-initdb.d。 "Initializing a fresh instance".
  2. 下提供了更多详细信息 here
  3. 使用 PodTemplateSpec 中的 initContainers 属性,类似:
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: statefulset-mydb
spec:
  serviceName: mydb-pod
  template:
  metadata:
    labels: 
    name: mydb-pod
  spec:
    containers:
    - name: aravo-database
      image: ignasiet/aravomysql
      ports:
        - containerPort: 3306
      volumeMounts:
        - name: volume-mydb
          mountPath: /var/lib/mysql
    initContainers:
    - name: aravo-database-init
      command:
        - /script/to/initialize/database
      image: ignasiet/aravomysql
      volumeMounts:
        - name: volume-mydb
          mountPath: /var/lib/mysql
    volumes:
    - name: volume-mydb
      persistentVolumeClaim: 
        claimName: config-mydb

您遇到的问题不是 StatefulSet 特有的。这是因为持久卷。如果你在没有持久卷的情况下使用 StatefulSet,你将不会遇到这个问题。或者,如果您使用 Deployment with persistent volume,您将面临这个问题。

为什么?好的,我来解释一下。

设置这些环境变量之一 MYSQL_ROOT_PASSWORDMYSQL_ALLOW_EMPTY_PASSWORDMYSQL_RANDOM_ROOT_PASSWORD 是创建新数据库所必需的。读取环境变量部分 here.

但是,如果您从脚本初始化数据库,则不需要提供它。看看这一行docker-entrypont.shhere。它检查 /var/lib/mysql 目录中是否已有数据库。如果有 none,它会尝试创建一个。如果您不提供任何指定的环境变量,那么它将给出您遇到的错误。但是,如果它发现那里已经有一个数据库,它就不会尝试创建一个,您也不会看到错误。

现在,问题是,你已经初始化了数据库,为什么它仍然抱怨环境变量?

在这里,持久卷发挥作用。由于您已将持久卷安装在 /var/lib/mysql 目录中,现在该目录指向当前为空的持久卷。因此,当您的容器 运行 docker-entrypoint.sh 脚本时,它没有在 /var/lib/mysql 目录中找到任何数据库,因为它现在指向持久卷而不是您的原始 /var/lib/mysql 目录docker 已在此目录上初始化数据库的图像。因此,它会尝试创建一个新数据库,并会抱怨你没有提供 MYSQL_ROOT_PASSWORD 环境变量。

当您不使用任何持久卷时,您的 /var/lib/mysql 目录指向包含初始化数据库的原始目录。所以,你不会看到错误。

那么,如何正确初始化mysql数据库呢?

为了从脚本初始化MySQL,您只需将脚本放入/docker-entrypoint-initdb.d。只需使用 vanilla mysql 映像,将初始化脚本放入卷中,然后将卷安装在 /docker-entrypoint-initdb.d 目录中。 MySQL 将被初始化。

查看此答案以了解有关如何从脚本初始化的详细信息: