[英]How can I have a host and container read/write the same files with Docker?
我想將一個目錄從 Docker 容器批量掛載到我的工作站,所以當我從我的工作站編輯卷掛載中的內容時,它也會在容器中更新。 一般而言,它對於測試和開發 Web 應用程序非常有用。
但是我在容器中獲得了拒絕的權限,因為容器和主機中的 UID 不同。 Docker 的初衷不就是讓開發更快更容易嗎?
這個答案解決了我在將 Docker 容器卷安裝到我的工作站時面臨的問題。 但是通過這樣做,我對生產中不需要的容器進行了更改,這違背了在開發期間使用 Docker 的目的。
容器是Alpine Linux 、工作站Fedora 29 和編輯器Atom 。
題
還有另一種方法,這樣我的工作站和容器都可以讀/寫相同的文件嗎?
有多種方法可以做到這一點,但核心問題是綁定掛載不包括任何 UID 映射功能,主機上的 UID 是出現在容器內的內容,反之亦然。 如果這兩個 UID 不匹配,您將使用不同的 UID 讀取/寫入文件,並且可能會遇到權限問題。
選項 1:獲取 Mac 或在 VirtualBox 內部署 docker。 這兩種環境都有一個文件系統集成,可以動態更新 UID。 對於 Mac,這是使用OSXFS實現的。 請注意,這種便利會帶來性能損失。
選項 2:更改您的主機。 如果主機上的 UID 與容器內的 UID 匹配,您將不會遇到任何問題。 您只需在主機上的用戶上運行 usermod 以更改您的 UID,事情就會發生,至少在您在容器內運行具有不同 UID 的不同圖像之前。
選項 3:更改您的圖像。 有些人會將圖像修改為與其環境匹配的靜態 UID,通常是為了匹配生產中的 UID。 其他人將傳遞一個帶有--build-arg UID=$(id -u)
類的構建參數作為構建命令的一部分,然后是 Dockerfile 類似的內容:
FROM alpine
ARG UID=1000
RUN adduser -u ${UID} app
這樣做的缺點是每個開發人員可能需要不同的圖像,因此他們要么在每個工作站上本地構建,要么您集中構建多個圖像,一個用於開發人員中存在的每個 UID。 這兩者都不是理想的。
選項 4:更改容器 UID。 這可以在撰寫文件中完成,也可以在一次性容器上完成,例如docker run -u $(id -u) your_image
。 容器現在將使用新的 UID 運行,並且可以訪問卷中的文件。 但是,容器內的用戶名不一定會映射到您的 UID,這對於您在容器內運行的任何命令來說可能看起來很奇怪。 更重要的是,容器內用戶擁有的任何您沒有隱藏在卷中的文件都將具有原始 UID,並且可能無法訪問。
選項 5:放棄,以 root 身份運行所有內容,或將權限更改為 777,允許所有人無限制地訪問該目錄。 這不會映射到您應該如何在生產中運行事物,並且容器可能仍會寫入具有有限權限的新文件,使您無法在容器外部訪問它們。 這也會產生以 root 身份運行代碼或讓文件系統對主機上的任何用戶進行讀寫的安全風險。
選項 6:設置一個動態更新容器的入口點。 盡管不想更改您的圖像,但這是我首選的完整性解決方案。 您的容器確實需要以 root 身份啟動,但僅限於開發階段,並且應用程序仍將以用戶身份運行,與生產環境相匹配。 但是,該入口點的第一步是更改容器內用戶的 UID/GID 以匹配您卷的 UID/GID。 這類似於選項 4,但現在圖像中未被卷替換的文件具有正確的 UID,並且容器內的用戶現在將顯示更改后的 UID,因此像ls
這樣的命令會顯示容器內的用戶名,而不是一個 UID 可能映射到另一個用戶或根本沒有。 雖然這是對您的映像的更改,但代碼僅在開發中運行,並且僅作為為該開發人員設置容器的簡短入口點,之后容器內的過程將與生產環境中的過程相同。
為了實現這一點,我進行了以下更改。 首先,Dockerfile 現在包含一個 fix-perms 腳本和來自我推送到集線器的基本映像的 gosu(這是一個 Java 示例,但更改可移植到其他環境):
FROM openjdk:jdk as build
# add this copy to include fix-perms and gosu or install them directly
COPY --from=sudobmitch/base:scratch / /
RUN apt-get update \
&& apt-get install -y maven \
&& useradd -m app
COPY code /code
RUN mvn build
# add an entrypoint to call fix-perms
COPY entrypoint.sh /usr/bin/
ENTRYPOINT ["/usr/bin/entrypoint.sh"]
CMD ["java", "-jar", "/code/app.jar"]
USER app
entrypoint.sh 腳本調用 fix-perms 然后 exec 和 gosu 從 root 刪除到應用程序用戶:
#!/bin/sh
if [ "$(id -u)" = "0" ]; then
# running on a developer laptop as root
fix-perms -r -u app -g app /code
exec gosu app "$@"
else
# running in production as a user
exec "$@"
fi
開發者撰寫文件掛載卷並以 root 身份啟動:
version: '3.7'
volumes:
m2:
services:
app:
build:
context: .
target: build
image: registry:5000/app/app:dev
command: "/bin/sh -c 'mvn build && java -jar /code/app.jar'"
user: "0:0"
volumes:
- m2:/home/app/.m2
- ./code:/code
這個例子取自我在此處提供的演示文稿: https : //sudo-bmitch.github.io/presentations/dc2019/tips-and-tricks-of-the-captains.html#fix-perms
fix-perms 和其他示例的代碼可在我的基本映像存儲庫中找到: https : //github.com/sudo-bmitch/docker-base
由於容器中的 UID 已融入容器定義中,因此您可以安全地假設它們是相對靜態的。 在這種情況下,您可以使用機器 UID 和 GID 在您的主機系統中創建一個用戶。 將用戶更改為新帳戶,然后對文件進行編輯。 您的主機操作系統不會抱怨,因為它認為只是用戶訪問了自己的文件,而您的容器操作系統將看到相同的內容。
或者,您可以考慮以 root 用戶身份編輯這些文件。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.