修改 VOLUME 自動建立預設許可權問題

yuxiaojie發表於2019-12-17

修改 VOLUME 自動建立預設許可權問題

VOLUME 預設許可權問題

Dockerfile 中,如果需要將資料持久化到本地,則需要以 VOLUME 關鍵字掛載一個目錄,然後這個目錄下的檔案操作,都會持久化到本地中

如果掛載的目錄在本地中是不存在的,則 Docker 會自動幫助建立一個目錄,而此時是使用 root 的身份完成這個動作的,所以建立的目錄許可權屬於 root 使用者 及 root 使用者組,然後 Docker 中的執行服務如果是用其他使用者身份執行的,那麼對這個目錄進行寫入等操作的時候,就會提示使用者許可權不足

此時不改動 dockerfile 要快速解決的話

  1. 可以直接將本地對映的目錄通過 chmod -R 777 your/path 命令開放所有許可權,這樣操作並不安全,因為開放所有許可權意味著任意使用者都可以對這個目錄進行任意的操作,可能會有誤操作等等的安全問題

  2. 找到這個容器對應的使用者名稱,然後將目錄通過命令 chown 把所有權轉給找到的使用者,這樣對比上面的操作沒有問題,但是操作會麻煩不少

    # 找到映象的名稱
    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 應用編寫 Dockerfile

假定已經有了一個 python 的 flask 的應用,服務的專案結構如下:

./
├── 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
複製程式碼

在 entrypoint.sh 對目錄進行授權

#!/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 命令

使用 docker-compose 快速啟動

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

相關文章