将Django-MySQL环境部署至GKE
你好。
我是Class Act Infrastructure事业部的大塚。
之前,我将存储在Harbor中的Django容器镜像作为基础,将其部署到k8s集群中作为DaemonSet。但是,这个部署还不能实现数据的持久化。
Django在默认情况下将数据存储在一个名为sqlite3的Python基于数据库中。但是,如果Pod消失,数据库中的内容也会消失。此外,由于每个Pod拥有不同的数据,这也会成为一个问题。下面的图像简要展示了这个问题。
因此,我想在这次中将Django的数据库设置为MySQL,并进行简单验证以确认数据是否被持久化。
建立形象
我本来想将容器镜像存储在Harbor中,并使用Rook-Ceph来保存MySQL的数据,但是我使用的环境似乎有些问题…所以我想在Google云上重新构建。以下是构建的示意图。
作为步骤,首先使用Docker环境,确认如何将Django容器和MySQL容器进行协作。然后进行自定义,以便在k8s集群上部署。自定义完成后,将这些容器打成镜像并推送至Docker Hub。然后在k8s端进行拉取,并以Pod方式部署。
请参考以下网站
建立
使用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.
将容器转换为镜像
在此之前使用当前设置创建容器映像,并将其推送到容器仓库。
并且在此之前,将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
确认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.
确认数据是否已持久化。
删除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
为了确保一下,我会从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)