Dockerfile 中,若是须要将数据持久化到本地,则须要以 VOLUME 关键字挂载一个目录,而后这个目录下的文件操做,都会持久化到本地中python
若是挂载的目录在本地中是不存在的,则 Docker 会自动帮助建立一个目录,而此时是使用 root 的身份完成这个动做的,因此建立的目录权限属于 root 用户 及 root 用户组,而后 Docker 中的执行服务若是是用其余用户身份执行的,那么对这个目录进行写入等操做的时候,就会提示用户权限不足git
此时不改动 dockerfile 要快速解决的话github
能够直接将本地映射的目录经过 chmod -R 777 your/path
命令开放全部权限,这样操做并不安全,由于开放全部权限意味着任意用户均可以对这个目录进行任意的操做,可能会有误操做等等的安全问题docker
找到这个容器对应的用户名,而后将目录经过命令 chown
把全部权转给找到的用户,这样对比上面的操做没有问题,可是操做会麻烦很多shell
# 找到镜像的名称
docker images
# 经过交互模式在镜像中执行 bash 命令
docker run -it api_demo_api bash
# 查看镜像中的用户
root@20fcb7c63dee:/home/www# cat /etc/passwd
......
www:x:101:65534::/home/www:/bin/false
# 对本地映射的目录,受权为镜像中查看到的用户
chown -R 101 /data/apiDemo
复制代码
可是有个问题就是,这些操做都须要用户使用时进行,须要培训或提早文档告知,若是可以在编写 dockerfile 的时候直接解决这个问题,那无疑是最好的方式flask
假定已经有了一个 python 的 flask 的应用,服务的项目结构以下:api
./
├── Dockerfile
├── README.md
├── docker-compose.yml
├── entrypoint.sh
└── src
├── app
├── gun.py
├── requirements.txt
└── server.py
复制代码
服务基于 Python 3.6.6 镜像进行编写,dockerfile 以下:缓存
FROM python:3.6.6
# 安装依赖环境,单独 copy 一个文件,若是不改动这个文件,这一层产生的镜像均可以命中缓存
# 安装库比较费时间
COPY ./src/requirements.txt /home/app/requirements.txt RUN pip install --upgrade pip && pip install -r /home/app/requirements.txt
# 经过 ENTRYPOINT 关键字,在镜像服务启动以前执行一个脚本
COPY ./entrypoint.sh /usr/local/bin/ RUN chmod 755 /usr/local/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"]
# 建立一个镜像内的用户 www 及用户组 www,而且将用户 www 配置进 www 用户组中
RUN addgroup www && adduser --system www && adduser www www
# 将整个项目源码都 copy 进工做目录下
WORKDIR /home/www COPY ./src /home/www
# 在镜像中建立目录而且进行受权,而且将日志目录写入镜像的环境变量中后续使用
ENV LOG_DIR /log
RUN mkdir -p "$LOG_DIR" && chown -R www:www "$LOG_DIR" VOLUME /log 复制代码
#!/bin/sh
# 若是执行用户是 root 则进行受权操做
if [ "$(id -u)" = '0' ]; then
# !表示对结果取反,表示找出全部用户不是 www 的文件,最后的 + 表示将全部找出的文件一块儿执行 chown 命令
find "$LOG_DIR" \! -user www -exec chown www '{}' +
fi
# 执行 docker 传递进行来的命令,若是没有这行,docker执行完 entrypoint.sh 就会直接退出
exec "$@"
复制代码
注意:安全
运行程序是用建立的用户进行运行,可是 Dockerfile 及 entrypoint.sh 脚本执行的过程,所有都是经过 root 用户来进行执行的,否则也没有权限进行受权,因此这里都没有使用 Dockerfile 的
USER
命令bash
version: '3.7'
services:
api:
build: ./
command: gunicorn -c gun.py server:app
ports:
- 12345:12345
environment:
- SERVER_ENV={$SERVER_ENV}
- HOST_ID={$HOST_ID}
volumes:
- "/data/apiDemo:/log"
复制代码
注意,用户是经过 root 权限运行的,因此执行命令须要以其余用户权限执行的操做须要由执行命令来完成,咱们这里使用 gunicorn 来做为容器启动服务的,因此就在 gunicorn 的配置文件 gun.py中进行配置,配置文件参考以下:
import multiprocessing
import os
from app.config import LOG_PATH
# 指定运行用户身份
user = 'www'
group = 'www'
debug = False
deamon = False
loglevel = 'info'
bind = '0.0.0.0:12345'
max_requests = 50000
worker_connections = 50000
x_forwarded_for_header = "X-Real-IP"
# 启动的进程数
workers = multiprocessing.cpu_count()
# workers = 3
worker_class = "gevent"
# 日志写入目录配置为受权的目录日志目录
accesslog = os.path.join(LOG_PATH, 'access.log')
access_log_format = '%({X-Real-IP}i)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
errorlog = os.path.join(LOG_PATH, 'error.log')
timeout = 60
复制代码
# 执行命令进行打包和运行
docker-compose build
docker-compose up
# 查看写入的日志文件
ll /data/
drwxr-xr-x 2 101 ssh_keys 4096 12月 17 15:09 apiDemo
ll /data/apiDemo/
-rw-r--r-- 1 101 ssh_keys 82 12月 17 15:08 access.log
-rw-r--r-- 1 101 ssh_keys 64 12月 17 15:08 api.log
-rw-r--r-- 1 101 ssh_keys 914 12月 17 16:12 error.log
复制代码
完整项目的配置能够参考: api_demo