• 【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
  • 相关阅读:
    接收网络视频数据并解码的探索
    git命令之追溯文件修改记录:git blame 和 git show【笔记】
    MapReduce之WordCount程序打包
    Elastic Search 浅浅认识 快速使用 keyword 和 text 的区别之处 spring boot 集成案例 es 增删改查
    Mac安装多个版本Node.js
    SEO的了解
    立体多层玫瑰绘图源码__玫瑰花python 绘图源码集锦
    学点设计模式,盘点Spring等源码中与设计模式的那些事之创建型模式
    java性能优化总结
    STM32物联网项目-HMI串口屏
  • 原文地址:https://blog.csdn.net/m0_46629123/article/details/127426212