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