docker 生产环境中的文件写入失败
File writing failing in docker production environment
在我的生产环境中,我无法写入文件。例如,我用 Celery 设置了一个测试任务,每分钟将时间写入一个文件:
@celery_app.task(name='print_time')
def print_time():
now = datetime.datetime.now().strftime('%Y %b %d %a @%H:%M')
cur_time = {"now": now}
print(f'The date and time sent: {cur_time}')
json.dump(cur_time, open(PATH.abspath(PATH.join(APP_DIR, "data", "cur_time.json")), "w"))
t = json.load(open(PATH.abspath(PATH.join(APP_DIR, "data", "cur_time.json"))))
print(f'The date and time received: {t}')
两个打印语句都会给出预期的结果,在我写这篇文章时,它们最后打印的是:
The date and time sent: {'now': '2021 May 26 Wed @18:57'}
The date and time received: {'now': '2021 May 26 Wed @18:57'}
但是,当我设置一个视图来显示内容时:
class TimeView(TemplateView):
def get_context_data(self, **kwargs):
time = json.load(open(PATH.abspath(PATH.join(APP_DIR, "data", "cur_time.json"))))
return time
很明显,当我转到 url 时,文件并没有在开发环境中真正更新,并且时间继续保持与我最初从我的开发环境中同步文件时的时间相同(正在成功更新文件内容)
为了进一步验证这一点,我还 运行 cat cur_time.json
和 stat cur_time.json
来验证文件没有被成功写入。
知道文件没有更新,我的问题有两个。一,为什么我在 celery 任务中的打印语句打印结果就像文件正在更新一样?二、这个问题最可能的原因和解决方法是什么?
我认为这与我的 Docker 容器文件写入权限有关,但我已经 运行 chmod -R 777 data
更改了数据目录中的写入权限。此外,我还没有收到任何权限错误消息,当权限是手头的问题时,这些消息似乎会被抛出。我开始触及知识的极限,想知道是否有人知道 problem/solution 可能是什么。谢谢
编辑回复评论:
我正在使用 docker-compose。这是我的 production.yml 文件:
version: '3'
volumes:
production_postgres_data: {}
production_postgres_data_backups: {}
production_traefik: {}
services:
django: &django
build:
context: .
dockerfile: ./compose/production/django/Dockerfile
image: myapp_production_django
depends_on:
- postgres
- redis
env_file:
...
command: /start
postgres:
...
traefik:
...
redis:
image: redis:5.0
celeryworker:
<<: *django
image: myapp_production_celeryworker
command: /start-celeryworker
celerybeat:
<<: *django
image: myapp_production_celerybeat
command: /start-celerybeat
flower:
<<: *django
image: myapp_production_flower
command: /start-flower
第二次编辑回应评论:
这是我的 local.yml 文件的视图
version: '3'
volumes:
local_postgres_data: {}
local_postgres_data_backups: {}
services:
django: &django
build:
context: .
dockerfile: ./compose/local/django/Dockerfile
image: myapp_local_django
container_name: django
depends_on:
- postgres
volumes:
- .:/app:z
env_file:
...
ports:
- "8000:8000"
command: /start
postgres:
build:
context: .
dockerfile: ./compose/production/postgres/Dockerfile
image: myapp_production_postgres
container_name: postgres
volumes:
- local_postgres_data:/var/lib/postgresql/data:Z
- local_postgres_data_backups:/backups:z
env_file:
...
redis:
image: redis:5.0
container_name: redis
celeryworker:
<<: *django
image: myapp_local_celeryworker
container_name: celeryworker
depends_on:
- redis
- postgres
ports: []
command: /start-celeryworker
celerybeat:
<<: *django
image: myapp_local_celerybeat
container_name: celerybeat
depends_on:
- redis
- postgres
ports: []
command: /start-celerybeat
flower:
<<: *django
image: myapp_local_flower
container_name: flower
ports:
- "5555:5555"
command: /start-flower
给予应有的信任。 @IainShelvington 在上面的评论中优雅地提出了问题和解决方案。
问题原因:“您在 docker 容器中写入的任何文件都不会写入主机,除非您装载卷并写入该卷。”
问题的解决方案:“在您的撰写配置中向全局“卷:”添加一个新卷。将该卷安装在“django”服务中,所有 celery 服务都继承自该服务,因此它应该被共享。从您安装的位置写入和读取文件(这应该与应用程序安装完全不同,例如“/celery-logs”或其他东西)
为了演示这个解决方案在我的具体示例中的样子,我将以下内容添加到我的 production.yml 文件中:
volumes:
...
production_celery: {}
services:
django: &django
build:
...
image: myapp_production_django
depends_on:
...
volumes:
- production_celery:/app/celerydata:z
env_file:
...
command: /start
然后,从我的 celery 脚本派生的所有数据文件都被发送到名为“celerydata”的新 volume/directory 并从中提取
如评论中所述,我的应用程序以前依赖于 APScheduler,我已经习惯于快速将数据文件写入主机并能够轻松浏览它们。为了再次在主机上查看它们并作为安全预防措施(数据冗余),我开始使用以下命令序列将文件从 celerydata 目录复制到我的本地机器,在那里我可以更轻松地查看它们图形界面:
docker ps # note container_id == ${CID} below
export CID=foobarbaz123
docker cp ${CID}:/app/celerydata ./celery_storage
在未来的某个时候,我可能会在启动容器时将其制作成 运行 的脚本,并相应地更新答案。
在我的生产环境中,我无法写入文件。例如,我用 Celery 设置了一个测试任务,每分钟将时间写入一个文件:
@celery_app.task(name='print_time')
def print_time():
now = datetime.datetime.now().strftime('%Y %b %d %a @%H:%M')
cur_time = {"now": now}
print(f'The date and time sent: {cur_time}')
json.dump(cur_time, open(PATH.abspath(PATH.join(APP_DIR, "data", "cur_time.json")), "w"))
t = json.load(open(PATH.abspath(PATH.join(APP_DIR, "data", "cur_time.json"))))
print(f'The date and time received: {t}')
两个打印语句都会给出预期的结果,在我写这篇文章时,它们最后打印的是:
The date and time sent: {'now': '2021 May 26 Wed @18:57'}
The date and time received: {'now': '2021 May 26 Wed @18:57'}
但是,当我设置一个视图来显示内容时:
class TimeView(TemplateView):
def get_context_data(self, **kwargs):
time = json.load(open(PATH.abspath(PATH.join(APP_DIR, "data", "cur_time.json"))))
return time
很明显,当我转到 url 时,文件并没有在开发环境中真正更新,并且时间继续保持与我最初从我的开发环境中同步文件时的时间相同(正在成功更新文件内容)
为了进一步验证这一点,我还 运行 cat cur_time.json
和 stat cur_time.json
来验证文件没有被成功写入。
知道文件没有更新,我的问题有两个。一,为什么我在 celery 任务中的打印语句打印结果就像文件正在更新一样?二、这个问题最可能的原因和解决方法是什么?
我认为这与我的 Docker 容器文件写入权限有关,但我已经 运行 chmod -R 777 data
更改了数据目录中的写入权限。此外,我还没有收到任何权限错误消息,当权限是手头的问题时,这些消息似乎会被抛出。我开始触及知识的极限,想知道是否有人知道 problem/solution 可能是什么。谢谢
编辑回复评论:
我正在使用 docker-compose。这是我的 production.yml 文件:
version: '3'
volumes:
production_postgres_data: {}
production_postgres_data_backups: {}
production_traefik: {}
services:
django: &django
build:
context: .
dockerfile: ./compose/production/django/Dockerfile
image: myapp_production_django
depends_on:
- postgres
- redis
env_file:
...
command: /start
postgres:
...
traefik:
...
redis:
image: redis:5.0
celeryworker:
<<: *django
image: myapp_production_celeryworker
command: /start-celeryworker
celerybeat:
<<: *django
image: myapp_production_celerybeat
command: /start-celerybeat
flower:
<<: *django
image: myapp_production_flower
command: /start-flower
第二次编辑回应评论:
这是我的 local.yml 文件的视图
version: '3'
volumes:
local_postgres_data: {}
local_postgres_data_backups: {}
services:
django: &django
build:
context: .
dockerfile: ./compose/local/django/Dockerfile
image: myapp_local_django
container_name: django
depends_on:
- postgres
volumes:
- .:/app:z
env_file:
...
ports:
- "8000:8000"
command: /start
postgres:
build:
context: .
dockerfile: ./compose/production/postgres/Dockerfile
image: myapp_production_postgres
container_name: postgres
volumes:
- local_postgres_data:/var/lib/postgresql/data:Z
- local_postgres_data_backups:/backups:z
env_file:
...
redis:
image: redis:5.0
container_name: redis
celeryworker:
<<: *django
image: myapp_local_celeryworker
container_name: celeryworker
depends_on:
- redis
- postgres
ports: []
command: /start-celeryworker
celerybeat:
<<: *django
image: myapp_local_celerybeat
container_name: celerybeat
depends_on:
- redis
- postgres
ports: []
command: /start-celerybeat
flower:
<<: *django
image: myapp_local_flower
container_name: flower
ports:
- "5555:5555"
command: /start-flower
给予应有的信任。 @IainShelvington 在上面的评论中优雅地提出了问题和解决方案。
问题原因:“您在 docker 容器中写入的任何文件都不会写入主机,除非您装载卷并写入该卷。”
问题的解决方案:“在您的撰写配置中向全局“卷:”添加一个新卷。将该卷安装在“django”服务中,所有 celery 服务都继承自该服务,因此它应该被共享。从您安装的位置写入和读取文件(这应该与应用程序安装完全不同,例如“/celery-logs”或其他东西)
为了演示这个解决方案在我的具体示例中的样子,我将以下内容添加到我的 production.yml 文件中:
volumes:
...
production_celery: {}
services:
django: &django
build:
...
image: myapp_production_django
depends_on:
...
volumes:
- production_celery:/app/celerydata:z
env_file:
...
command: /start
然后,从我的 celery 脚本派生的所有数据文件都被发送到名为“celerydata”的新 volume/directory 并从中提取
如评论中所述,我的应用程序以前依赖于 APScheduler,我已经习惯于快速将数据文件写入主机并能够轻松浏览它们。为了再次在主机上查看它们并作为安全预防措施(数据冗余),我开始使用以下命令序列将文件从 celerydata 目录复制到我的本地机器,在那里我可以更轻松地查看它们图形界面:
docker ps # note container_id == ${CID} below
export CID=foobarbaz123
docker cp ${CID}:/app/celerydata ./celery_storage
在未来的某个时候,我可能会在启动容器时将其制作成 运行 的脚本,并相应地更新答案。