• 【Django-meeting系统】Racher部署教室管理系统的方法(一个pod部署应用、redis、celery)---20221019


    1.镜像

    docker build -t seasonzhang/meeting_booking:1.5.13 .
    docker push seasonzhang/meeting_booking:1.5.13

    2.settings.py

    设置环境变量和默认值

    """
    Django settings for classroom_management project.
    
    Generated by 'django-admin startproject' using Django 3.2.7.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/3.2/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/3.2/ref/settings/
    """
    
    from pathlib import Path
    import os
    from django.utils.translation import gettext_lazy as _
    
    # debug模式
    DJANGO_DEBUG = os.environ.get('DJANGO_DEBUG', True)
    
    # 设定redis是否使用
    REDIS_FALSE_TRUE = os.environ.get('REDIS_FALSE_TRUE', False)
    REDIS_LOCATION = os.environ.get('REDIS_LOCATION',"redis://default:redis654321@10.41.241.169:30394")
    CELERY_FALSE_TRUE = os.environ.get('CELERY_FALSE_TRUE', False)
    
    
    # 设定数据库是否使用MYSQL
    MYSQL_OR_NOT = os.environ.get('MYSQL_OR_NOT', False)
    MYSQL_IP = os.environ.get('MYSQL_IP', '10.41.241.169')
    MYSQL_PORT = os.environ.get('MYSQL_PORT', '30031')
    
    
    
    # Build paths inside the project like this: BASE_DIR / 'subdir'.
    BASE_DIR = Path(__file__).resolve().parent.parent
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = 'django-insecure-by14)kf_gs_minl3x(47v1!7fk39!i-q@p=nd)!pl-=eamtde^'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    # SECURITY WARNING: don't run with debug turned on in production!
    if DJANGO_DEBUG == True or DJANGO_DEBUG == 'True':
        DEBUG = True
    elif DJANGO_DEBUG == False or DJANGO_DEBUG == 'False':
        DEBUG = False
    # DEBUG = True
    # DEBUG = False
    
    
    ALLOWED_HOSTS = ['*']
    
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'simpleui',
        'myclassroom',
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework',
        'django_celery_beat',
    ]
    
    
    if REDIS_FALSE_TRUE:
        MIDDLEWARE = [
            'django.middleware.security.SecurityMiddleware',
            'django.contrib.sessions.middleware.SessionMiddleware',
            'django.middleware.locale.LocaleMiddleware',### 多语言中间件
            'django.middleware.cache.UpdateCacheMiddleware',#redis中间件
            'django.middleware.common.CommonMiddleware',
            'django.middleware.cache.FetchFromCacheMiddleware',#redis中间件
            'django.middleware.csrf.CsrfViewMiddleware',
            'django.contrib.auth.middleware.AuthenticationMiddleware',
            'django.contrib.messages.middleware.MessageMiddleware',
            'django.middleware.clickjacking.XFrameOptionsMiddleware',
        ]
    else:
        MIDDLEWARE = [
            'django.middleware.security.SecurityMiddleware',
            'django.contrib.sessions.middleware.SessionMiddleware',
            'django.middleware.locale.LocaleMiddleware',### 多语言中间件
            #'django.middleware.cache.UpdateCacheMiddleware',#redis中间件
            'django.middleware.common.CommonMiddleware',
            #'django.middleware.cache.FetchFromCacheMiddleware',#redis中间件
            'django.middleware.csrf.CsrfViewMiddleware',
            'django.contrib.auth.middleware.AuthenticationMiddleware',
            'django.contrib.messages.middleware.MessageMiddleware',
            'django.middleware.clickjacking.XFrameOptionsMiddleware',
        ]
    
    
    ROOT_URLCONF = 'classroom_management.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'classroom_management.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/3.2/ref/settings/#databases
    if MYSQL_OR_NOT:
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.mysql',   # 数据库引擎
                'NAME': 'season',  # 数据库名,先前创建的
                'USER': 'root',     # 用户名,可以自己创建用户
                'PASSWORD': '123456',  # 密码
                'HOST': MYSQL_IP,  # mysql服务所在的主机ip
                'PORT': MYSQL_PORT,         # mysql服务端口
            }
        }
    else:
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.sqlite3',
                #'NAME': BASE_DIR / 'db.sqlite3',
                'NAME': Path(__file__).resolve().parent /'SQL' /'db.sqlite3',
            }
        }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/3.2/topics/i18n/
    
    
    # 修改中文,Django内部设置zh_Hans方法指向中文 
    LANGUAGE_CODE = 'zh-hans' 
    # LANGUAGE_CODE = 'en-us'
    
    LANGUAGES = [
        ('zh-hans', _('Chinese')),
        ('en', _('English')),
    ]
    
    
    # 修改中国时区
    TIME_ZONE = 'Asia/Shanghai'
    
    LANGUAGE_CODE = 'zh-hans'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    #USE_TZ = False
    
    LOCALE_PATHS = (
        os.path.join(BASE_DIR, 'locale'),
    )
    
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/3.2/howto/static-files/
    # 通过url直接访问我在项目中的静态文件
    STATIC_URL = '/static/'
    # 部署静态文件时(pyhtonmanage.pycollectstatic)所有的静态文静聚合的目录
    STATIC_ROOT = os.path.join(BASE_DIR, "/static/")
    # STATICFILES_DIRS告诉django,首先到STATICFILES_DIRS里面寻找静态文件,其次再到各个app的static文件夹里面找
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, "static"),
    )
    
    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    
    
    
    # Default primary key field type
    # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
    
    DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
    
    
    
    LOGIN_URL = '/user_login/'
    
    DATE_FORMAT = 'Y-m-d'
    
    
    
    # Django Rest Framework框架设置信息
    # 分页设置
    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        # 每页显示多少条数据
        'PAGE_SIZE': 10000
    }
    
    
    
    # K8S redis
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": REDIS_LOCATION,
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
                "PASSWORD":"redis654321",
                "SOCKET_CONNECT_TIMEOUT": 5,  # in seconds
                "SOCKET_TIMEOUT": 5,  # r/w timeout in seconds
            }
        }
    }
    
    
    
    
    
    
    CELERY_BROKER_URL = REDIS_LOCATION
    CELERY_RESULT_BACKEND = REDIS_LOCATION
    CELERY_ACCEPT_CONTENT = ['application/json']
    CELERY_RESULT_SERIALIZER = 'json'
    CELERY_TASK_SERIALIZER = 'json'
    CELERY_TIMEZONE = 'Asia/Shanghai'
    CELERYD_MAX_TASKS_PER_CHILD = 10
    CELERYD_LOG_FILE = os.path.join(BASE_DIR, "logs", "celery_work.log")
    CELERYBEAT_LOG_FILE = os.path.join(BASE_DIR, "logs", "celery_beat.log")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262

    3.racher中部署

    3.1 用于在racher中部署【应用】的yaml档

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      annotations:
        kompose.cmd: kompose convert
        kompose.version: 1.24.0 (7c629530)
      creationTimestamp: null
      labels:
        io.kompose.service: meeting-booking-web
      name: meeting-booking-web
    spec:
      replicas: 1
      selector:
        matchLabels:
          io.kompose.service: meeting-booking-web
      strategy:
        type: Recreate
      template:
        metadata:
          annotations:
            kompose.cmd: kompose convert
            kompose.version: 1.24.0 (7c629530)
          creationTimestamp: null
          labels:
            io.kompose.service: meeting-booking-web
        spec:
          containers:
            - command:
                - /bin/sh
                - /code/package/start.sh
              image: seasonzhang/meeting_booking:1.5.13
              imagePullPolicy: IfNotPresent
              name: meeting-booking-web
              ports:
                - containerPort: 8000
              resources:
                requests:
                  cpu: 500m
                  memory: 500Mi
                limits:
                  cpu: 500m
                  memory: 500Mi
            - image: redis
              name: myredis
              command:
                - redis-server
                - '--requirepass'
                - 'redis654321'		# 初始密码
              ports:
                - containerPort: 6379
              resources:
                requests:
                  cpu: 100m
                  memory: 100Mi
                limits:
                  cpu: 200m
                  memory: 200Mi
            - command:
                - /bin/sh
                - /code/package/start-beat.sh
              image: seasonzhang/meeting_booking:1.5.13
              name: meeting-booking-beat
              resources:
                requests:
                  cpu: 100m
                  memory: 100Mi
                limits:
                  cpu: 200m
                  memory: 200Mi
            - command:
                - /bin/sh
                - /code/package/start-celery.sh
              image: seasonzhang/meeting_booking:1.5.13
              name: meeting-booking-celery
              resources:
                requests:
                  cpu: 100m
                  memory: 100Mi
                limits:
                  cpu: 200m
                  memory: 200Mi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    在这里插入图片描述

    3.2 设定meeting-booking-web的环境变量,再设定meeting-booking-celery的环境变量

    meeting-booking-web的环境变量
    在这里插入图片描述
    meeting-booking-celery的环境变量
    在这里插入图片描述

    3.3 建立svc和ingress

    svc
    在这里插入图片描述

    ingress
    在这里插入图片描述

    4.遇到的坑

    4.1 变量有三种来源:默认值、config、环境变量,每个container的变量是独立的。

    情形说明:设定meeting-booking-web的环境变量后,没有设定meeting-booking-celery的环境变量,结果meeting-booking-celery一直读取默认值。debug花费了我一周时间。

    • meeting-booking-celery的container老是读到默认值,气死
      在这里插入图片描述
    • 直到我发现在meeting-booking-web的container中启动celery,居然读到正确的redis地址。
      在这里插入图片描述
    • 这时候,我才想起来变量有三种来源:默认值、config、环境变量
    • 先把config的变量干掉
      在这里插入图片描述
      在这里插入图片描述
    • 再设定meeting-booking-celery的环境变量,避免读取镜像的默认值。
      在这里插入图片描述
    • 之后就成功可以启动celery,并发送异步邮件。
      在这里插入图片描述

    4.2 利用4.1方法启动的celery异步邮件,是直接读取原始镜像SQL的邮件。而不直接读取PVC的SQLite。

    在这里插入图片描述
    所以邮件的内容不能在部署在racher中的应用admin后台更改!
    所以邮件的内容不能在部署在racher中的应用admin后台更改!
    所以邮件的内容不能在部署在racher中的应用admin后台更改!

    • 以下改了只会在meeting-booking-web中生效,而不会在meeting-booking-celery生效!
      在这里插入图片描述
    • 所以邮件内容必须在原始镜像SQL的邮件设置中改好!
      在这里插入图片描述

    (debug)4.2 可以利用新建临时PVC来保存更新后的SQLite(每次更新的SQLite都需要新建临时PVC来同步给celery)。

    在这里插入图片描述

    步骤1:建立临时PVC
    在这里插入图片描述

    步骤2:COPY SQLite给PVC
    在这里插入图片描述
    cp /code/package/classroom_management/SQL/db.sqlite3 /data2/
    在这里插入图片描述

    步骤3:部署应用时挂临时PVC(可选)

    步骤4:将临时PVC改到celery的容器
    在这里插入图片描述
    步骤5:发送异步邮件来测试
    在这里插入图片描述
    步骤6:查看PVC
    meeting-booking-web的PVC
    在这里插入图片描述
    meeting-booking-beat的PVC
    在这里插入图片描述

    4.3 celery发送异步邮件,会出现线程的错误!

    目前我没有解法,但是不影响celery异步邮件发送。

    [2022-10-20 13:56:15,162: WARNING/MainProcess] send_apply_email
    [2022-10-20 13:56:15,167: ERROR/MainProcess] Task myclassroom.ExchangeEmailSent.send_apply_email[e324fcc7-11a3-42c0-b00b-39a5f88232bd] raised unexpected: DatabaseError("DatabaseWrapper objects created in a thread can only be used in that same thread. The object with alias 'default' was created in thread id 140050967925680 and this is thread id 140050872465712.")
    Traceback (most recent call last):
      File "/usr/local/lib/python3.8/site-packages/celery/app/trace.py", line 412, in trace_task
        R = retval = fun(*args, **kwargs)
      File "/usr/local/lib/python3.8/site-packages/celery/app/trace.py", line 704, in __protected_call__
        return self.run(*args, **kwargs)
      File "/code/package/myclassroom/ExchangeEmailSent.py", line 169, in send_apply_email
        print('webmail_items.first().hradmin_namelist',webmail_items.first().hradmin_namelist.strip("'").split(';'))
      File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 674, in first
        for obj in (self if self.ordered else self.order_by('pk'))[:1]:
      File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 280, in __iter__
        self._fetch_all()
      File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 1324, in _fetch_all
        self._result_cache = list(self._iterable_class(self))
      File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 51, in __iter__
        results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
      File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1173, in execute_sql
        cursor = self.connection.cursor()
      File "/usr/local/lib/python3.8/site-packages/django/utils/asyncio.py", line 26, in inner
        return func(*args, **kwargs)
      File "/usr/local/lib/python3.8/site-packages/django/db/backends/base/base.py", line 259, in cursor
        return self._cursor()
      File "/usr/local/lib/python3.8/site-packages/django/db/backends/base/base.py", line 237, in _cursor
        return self._prepare_cursor(self.create_cursor(name))
      File "/usr/local/lib/python3.8/site-packages/django/db/backends/base/base.py", line 227, in _prepare_cursor
        self.validate_thread_sharing()
      File "/usr/local/lib/python3.8/site-packages/django/db/backends/base/base.py", line 552, in validate_thread_sharing
        raise DatabaseError(
    django.db.utils.DatabaseError: DatabaseWrapper objects created in a thread can only be used in that same thread. The object with alias 'default' was created in thread id 140050967925680 and this is thread id 140050872465712.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    (debug)4.3 celery发送异步邮件,会出现线程的错误!

    我发现我把beat的环境变量和PVC设完之后,celery的多线程报错也消失了
    在这里插入图片描述
    在这里插入图片描述
    设完之后,不报错了!

    Task myclassroom.ExchangeEmailSent.send_agree_email[29042ebf-4618-479b-bfee-de640622ee78] succeeded in 0.3765974959824234s: None 
    
    • 1
  • 相关阅读:
    ES6-扩展运算符“...“
    代码+案例,实战解析BeautifulSoup4
    成为理事 | 零数科技正式加入可信区块链推进计划(TBI)成为理事成员
    如何使用 C++ 构建一个环结构?
    Linux学习第24天:Linux 阻塞和非阻塞 IO 实验(二): 挂起
    华硕主板升级更新BIOS版本
    【linux编程】linux文件IO高级I/O函数介绍和代码示例
    【EI会议征稿】第三届计算建模、仿真与数据分析国际学术会议(CMSDA 2023)
    力扣884. 两句话中的不常见单词--python用2行代码实现
    UE4 Unlua源码解析10 - Lua怎么替换BlueprintImplementableEvent或BlueprintNativeEvent的方法实现的
  • 原文地址:https://blog.csdn.net/m0_46629123/article/details/127426212