将Django-MySQL环境部署至GKE

你好。
我是Class Act Infrastructure事业部的大塚。

之前,我将存储在Harbor中的Django容器镜像作为基础,将其部署到k8s集群中作为DaemonSet。但是,这个部署还不能实现数据的持久化。

 

Django在默认情况下将数据存储在一个名为sqlite3的Python基于数据库中。但是,如果Pod消失,数据库中的内容也会消失。此外,由于每个Pod拥有不同的数据,这也会成为一个问题。下面的图像简要展示了这个问题。

k8s_3-ページ19.drawio.png

因此,我想在这次中将Django的数据库设置为MySQL,并进行简单验证以确认数据是否被持久化。

建立形象

我本来想将容器镜像存储在Harbor中,并使用Rook-Ceph来保存MySQL的数据,但是我使用的环境似乎有些问题…所以我想在Google云上重新构建。以下是构建的示意图。

k8s-04.drawio.png

作为步骤,首先使用Docker环境,确认如何将Django容器和MySQL容器进行协作。然后进行自定义,以便在k8s集群上部署。自定义完成后,将这些容器打成镜像并推送至Docker Hub。然后在k8s端进行拉取,并以Pod方式部署。

k8s-04-ページ1のコピー.drawio.png

请参考以下网站

 

建立

使用Docker环境创建Django+MySQL环境。

MySQL容器的配置

执行以下命令,部署MySQL容器。

root@k8s-master:~# docker run --name mysql -e MYSQL_USER=admin  -e MYSQL_PASSWORD=password -e MYSQL_ROOT_PASSWORD=password -d -p 3306:3306 mysql:latest

接下来,访问已部署的MySQL容器并创建一个名为“django_db”的数据库。

root@k8s-master:~# docker exec -it mysql /bin/bash
bash-4.4# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.33 MySQL Community Server - GPL
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> create database django_db;
Query OK, 1 row affected (0.01 sec)
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| django_db          |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.01 sec)

Django容器的配置

首先,从之前创建的Django 1.1映像文件部署Django容器。
接着,在部署的容器上执行以下命令安装mysqlclient。

root@843b59a4d154:/# apt-get install python3-dev default-libmysqlclient-dev 
root@843b59a4d154:/# pip install mysqlclient

root@843b59a4d154:/# pip list installed
Package             Version
------------------- -------------
asgiref             3.7.2
blinker             1.4
cryptography        3.4.8
dbus-python         1.2.18
distro              1.7.0
distro-info         1.1build1
Django              4.2.3
httplib2            0.20.2
importlib-metadata  4.6.4
jeepney             0.7.1
keyring             23.5.0
launchpadlib        1.10.16
lazr.restfulclient  0.14.4
lazr.uri            1.0.6
more-itertools      8.10.0
mysqlclient         2.1.1
oauthlib            3.2.0
pip                 22.0.2
PyGObject           3.42.1
PyJWT               2.3.0
pyparsing           2.4.7
python-apt          2.4.0+ubuntu1
SecretStorage       3.3.1
setuptools          59.6.0
six                 1.16.0
sqlparse            0.4.4
typing_extensions   4.7.1
unattended-upgrades 0.1
wadllib             1.3.6
wheel               0.37.1
zipp                1.0.0

接下来,我们将修改Django的settings.py文件。
通过将ENGINE的部分从sqlite3更改为mysql,声明要将MySQL作为数据库使用。只需在HOST部分设置为分配给MySQL容器的IP地址即可。

root@3a1c5e7cd2a3:/home/testProject/testProject# cp -p settings.py settings.py.2023071401
root@3a1c5e7cd2a3:/home/testProject/testProject# vi settings.py
root@3a1c5e7cd2a3:/home/testProject/testProject# diff settings.py settings.py.2023071401
78,83c78,79
<         'ENGINE': 'django.db.backends.mysql',
<         'NAME': 'django_db', 
<         'USER': 'root', 
<         'PASSWORD ': 'password',
<         'HOST': '172.17.0.3',
<         'PORT': '3306',
---
>         'ENGINE': 'django.db.backends.sqlite3',
>         'NAME': BASE_DIR / 'db.sqlite3',

确认Django是否与MySQL进行了连接。

在Django容器中执行以下命令来执行迁移。如果显示OK,则表示已经成功进行了协作。

root@843b59a4d154:/home# cd /home/testProject/
root@843b59a4d154:/home/testProject# python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

创建一个用于访问Django管理界面的用户。我们将其注册为admin:password。

root@843b59a4d154:/home/testProject# python3 manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@example.com
Password:
Password (again):
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

我会运行服务器。

root@843b59a4d154:/home/testProject# python3 manage.py runserver 0.0.0.0:8000
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
July 14, 2023 - 08:02:05
Django version 4.2.3, using settings 'testProject.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
image (2).png

将容器转换为镜像

在此之前使用当前设置创建容器映像,并将其推送到容器仓库。
并且在此之前,将settings.py中的HOST更改为将要使用的k8s的ClusterIP。

root@843b59a4d154:/home/testProject/testProject# diff settings.py settings.py.2023071401
78,83c78,79
<         'ENGINE': 'django.db.backends.mysql',
<         'NAME': 'django_db',
<         'USER': 'root',
<         'PASSWORD': 'password',
<         'HOST': 'mysql-clusterip',
<         'PORT': '3306',
---
>         'ENGINE': 'django.db.backends.sqlite3',
>         'NAME': BASE_DIR / 'db.sqlite3',

这次的操作是将镜像推送到Docker Hub上,但是具体步骤将被省略。

在GKE环境中部署Django-MySQL环境。

使用准备好的yaml和部署流程。

这是一个用于创建名为ins-env的namespace的yaml文件。

kind: Namespace 
apiVersion: v1 
metadata: 
  name: ins-env 
  labels: 
    name: ins-env

这是创建StorageClass及其关联PVC的yaml文件。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: mysql-storage-class
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard
  fstype: ext4
  replication-type: none
reclaimPolicy: Retain

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-vol-pvc
  namespace: ins-env
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: mysql-storage-class
  resources:
    requests:
      storage: 50Gi

我定义了用于 MySQL 的 ReplicaSet 的 ConfigMap,该 ConfigMap 包含必要的环境变量,以及用于访问 ReplicaSet 的 ClusterIP。

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-cm
  namespace: ins-env
data:
  MYSQL_USER : admin 
  MYSQL_PASSWORD : password 
  MYSQL_ROOT_PASSWORD : password
  MYSQL_DATABASE : django_db

---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: mysq-rs 
  namespace: ins-env
  labels:
    app: db 
spec:
  replicas: 1 
  selector:
    matchLabels:
      app: django-db 
  template:
    metadata:
      labels:
         app: django-db 
    spec:
      containers:
      - name: django-mysql 
        image: shotaohtsuka/mysql:1.0.0
        ports:
        - containerPort: 3306
        envFrom:
        - configMapRef:
            name: mysql-cm
        volumeMounts:
          - mountPath: /var/lib/mysql
            name: mysql-vol-pvc
      volumes: 
      - name: mysql-vol-pvc 
        persistentVolumeClaim: 
          claimName: mysql-vol-pvc
---
apiVersion: v1  
kind: Service  
metadata:  
  name: mysql-clusterip  
  namespace: ins-env
spec:  
  selector:  
    app: django-db 
  type: ClusterIP  
  ports:  
  - name: django-mysql-port   
    port: 3306  
    protocol: TCP  
    targetPort: 3306

这是用于部署Django的DaemonSet以及用于从Kubernetes集群外部连接该Django的LoadBalancer的yaml文件。

apiVersion: apps/v1 
kind: DaemonSet 
metadata: 
  name: django-rs 
  namespace: ins-env 
  labels: 
    app: django-web 
spec: 
  selector: 
    matchLabels: 
      name: django 
  template: 
    metadata: 
      labels: 
        name: django 
    spec: 
      containers: 
        - name: django-pod 
          image: shotaohtsuka/django:1.1.1
          ports: 
          - containerPort: 8000 
          command: ["python3", "/home/testProject/manage.py", "runserver", "0.0.0.0:8000"] 

---
apiVersion: v1 
kind: Service 
metadata: 
  name: django-loadbalancer 
  namespace: ins-env
spec: 
  type: LoadBalancer 
  selector: 
    name: django
  ports: 
  - protocol: TCP 
    port: 60000 
    targetPort: 8000

我們會進行部署。

ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl apply -f ns.yaml
ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl apply -f sc-pvc.yaml
ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl apply -f mysql.yaml 

ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl get cm -n ins-env
NAME               DATA   AGE
kube-root-ca.crt   1      34m
mysql-cm           4      42s

ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl apply -f django.yaml 

ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl get all -n ins-env
NAME                  READY   STATUS    RESTARTS   AGE
pod/django-rs-2j5vs   1/1     Running   0          42s
pod/django-rs-5qzhz   1/1     Running   0          42s
pod/django-rs-x5sxk   1/1     Running   0          42s
pod/mysq-rs-ndhxw     1/1     Running   0          77m
NAME                          TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)           AGE
service/django-loadbalancer   LoadBalancer   10.62.3.58    34.172.205.101   60000:31797/TCP   42s
service/mysql-clusterip       ClusterIP      10.62.0.195   <none>           3306/TCP          77m
NAME                       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/django-rs   3         3         3       3            3           <none>          42s
NAME                      DESIRED   CURRENT   READY   AGE
replicaset.apps/mysq-rs   1         1         1       77m
image (28).png

确认Django能够与MySQL进行通信。

在我的环境中部署了3个Django的Pod,我们将连接到适当的Pod上,并重新执行migrate和createsuperuser操作。如果成功执行,就说明可以与MySQL通信了。
* 尽管在Docker环境下也进行了此操作,但在那个时候没有考虑到数据的持久化,所以需要重新执行一次。

ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl get pod -n ins-env
NAME              READY   STATUS    RESTARTS   AGE
django-rs-2j5vs   1/1     Running   0          8m29s
django-rs-5qzhz   1/1     Running   0          8m29s
django-rs-x5sxk   1/1     Running   0          8m29s
mysq-rs-ndhxw     1/1     Running   0          84m
ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl exec -it django-rs-2j5vs -n ins-env -- /bin/bash

root@django-rs-2j5vs:/# cd /home/testProject/
root@django-rs-2j5vs:/home/testProject# python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

root@django-rs-2j5vs:/home/testProject# python3 manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@example.com
Password: 
Password (again): 
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
image (30).png

确认数据是否已持久化。

image (31).png

删除MySQL的pod。由于我们是通过ReplicaSet进行部署的,因此当pod被删除时,新的pod将自动部署。

ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl get pod -n ins-env | grep -i my
mysq-rs-ndhxw     1/1     Running   0          97m
ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl delete pod mysq-rs-ndhxw -n ins-env
pod "mysq-rs-ndhxw" deleted
ohtsuka_honban@cloudshell:~/yaml/ins-env (western-antonym-386513)$ kubectl get pod -n ins-env | grep -i my
mysq-rs-xtjfc     1/1     Running   0          10s
image (32).png

为了确保一下,我会从MySQL pod中确认一下是否有数据。

ohtsuka_honban@cloudshell:~ (western-antonym-386513)$ kubectl get pod -n ins-env | grep -i my
mysql-rs-nkp2j    1/1     Running   0          179m
ohtsuka_honban@cloudshell:~ (western-antonym-386513)$ kubectl exec -it mysql-rs-nkp2j -n ins-env -- /bin/bash
bash-4.4# mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 8.0.33 MySQL Community Server - GPL

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| django_db          |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.01 sec)

mysql> use django_db;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

mysql> show tables;
+----------------------------+
| Tables_in_django_db        |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+
10 rows in set (0.00 sec)

mysql> SELECT * FROM auth_user;
+----+------------------------------------------------------------------------------------------+----------------------------+--------------+------------+------------+-----------+-------------------+----------+-----------+----------------------------+
| id | password                                                                                 | last_login                 | is_superuser | username   | first_name | last_name | email             | is_staff | is_active | date_joined                |
+----+------------------------------------------------------------------------------------------+----------------------------+--------------+------------+------------+-----------+-------------------+----------+-----------+----------------------------+
|  1 | pbkdf2_sha256$600000$y4k9BAyTCjVjmZIMGILIOg$qG9T19bf4deVANCXaNji13AGDhbmzRIDwj+IERcTL14= | 2023-07-15 01:43:01.667938 |            1 | admin      |            |           | admin@example.com |        1 |         1 | 2023-07-15 01:41:02.644627 |
|  2 | pbkdf2_sha256$600000$OQ93zEhnCvE4th0oLhuWpM$DfRyLu1EoaBaMo2D0AXRn+51djN9UUuxFHHHG/LXlmQ= | NULL                       |            0 | k8s-django |            |           |                   |        0 |         1 | 2023-07-15 01:45:32.531639 |
+----+------------------------------------------------------------------------------------------+----------------------------+--------------+------------+------------+-----------+-------------------+----------+-----------+----------------------------+
2 rows in set (0.00 sec)
广告
将在 10 秒后关闭
bannerAds