一:容器文件操作
在Docker環境中,管理容器內部的文件是一個常見的需求。
無論是為了配置應用、備份數據還是調試問題,了解如何高效地進行文件操作都是非常重要的。
docker cp
命令提供了一種簡單的方法來在宿主主機和容器之間復制文件或目錄。
快速文件傳輸:
-
當我們需要快速地在宿主主機和容器之間傳輸文件,而不想通過網絡或構建新的鏡像時,
docker cp
提供了一種簡單直接的方法。
臨時性文件操作:
-
對于臨時性的文件修改或者調試,比如臨時修改配置文件、測試新功能等,
docker cp
可以讓我們迅速地將文件傳入傳出容器,無需重啟容器或重新構建鏡像。
其基本語法如下:
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH
-
CONTAINER
: 目標容器的名稱或ID。 -
SRC_PATH
: 源路徑,可以是宿主主機上的路徑或是容器內的路徑。 -
DEST_PATH
: 目標路徑,同樣可以是宿主主機上的路徑或是容器內的路徑。
1、Nginx案例
啟動Nginx容器
首先,我們需要啟動一個Nginx容器:
docker run --name mynginx -d nginx
從宿主主機復制文件到Nginx容器
假設我們有一個名為index.html
的靜態網頁文件想要放到Nginx的默認網頁目錄中(通常是/usr/share/nginx/html
)
我們可以使用以下命令:
docker cp index.html mynginx:/usr/share/nginx/html/
從Nginx容器復制文件到宿主主機
如果我們想從容器內獲取某個文件(例如修改后的index.html
),我們可以這樣做:
docker cp mynginx:/usr/share/nginx/html/index.html .
這將把容器內的index.html
文件復制到當前工作目錄下。
2、MySQL案例
準備一個 .sql
文件
-- init.sql 示例內容
CREATE DATABASE IF NOT EXISTS testdb;
USE testdb;CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100)
);INSERT INTO users (name) VALUES ('Alice'), ('Bob');
啟動MySQL容器
使用官方的MySQL鏡像啟動一個新的容器,并設置root用戶的密碼:
docker run --name my-mysql \-e MYSQL_ROOT_PASSWORD=123456 \-p 13306:3306 \-d mysql:8.0.22 \--default-authentication-plugin=mysql_native_password \--character-set-server=utf8mb4 \--collation-server=utf8mb4_unicode_ci
參數 | 含義 |
---|---|
--name my-mysql | 給容器起個名字叫 my-mysql |
-e MYSQL_ROOT_PASSWORD=123456 | 設置MySQL的root用戶密碼為 123456 |
-p 13306:3306 | 將宿主機的 13306 端口映射到容器的 3306 端口(避免與本機MySQL沖突) |
-d mysql:8.0.22 | 后臺運行名為 mysql:8.0.22 的鏡像 |
--default-authentication-plugin=mysql_native_password | 使用兼容性更好的認證插件 |
--character-set-server=utf8mb4 | 設置默認字符集為 utf8mb4 支持中文和表情符號 |
--collation-server=utf8mb4_unicode_ci | 設置排序規則 |
將本地 SQL 文件復制到容器中
# 假設我們的 init.sql 在 /root/mysql_demo 目錄下
docker cp /root/mysql_demo/init.sql my-mysql:/tmp/init.sql
在容器中執行 SQL 文件導入數據
docker exec -it my-mysql mysql -u root -p123456 -e "source /tmp/init.sql"
導出數據庫為新的 SQL 文件
docker exec my-mysql mysqldump -u root -p123456 --all-databases > /root/mysql_demo/backup_full.sql
docker exec my-mysql mysqldump -u root -p123456 testdb > /root/mysql_demo/backup_testdb.sql
也可以先在容器里生成備份再拷貝出來
# 在容器內生成備份
docker exec my-mysql sh -c 'mysqldump -u root -p"123456" testdb > /tmp/testdb_backup.sql'?
# 把備份文件復制到宿主機
docker cp my-mysql:/tmp/testdb_backup.sql /root/mysql_demo/testdb_backup.sql
docker cp
適用于小規模的文件傳輸。如果涉及到大量數據的導入導出,這種方法可能不夠高效,且容易出錯。例如,在處理數據庫備份時,雖然可以先通過命令行工具生成備份文件再用
docker cp
拷貝出來,但對于非常大的數據庫,這并不是最理想的選擇。
?
如果依賴
docker cp
來進行所有文件交換,那么隨著容器的銷毀,這些手動拷貝的數據也會丟失。因此,對于需要長期存儲的數據,應該考慮使用數據卷(data volumes)或綁定掛載(bind mounts),而不是僅依賴
docker cp
。
3、容器持久化存儲
寫時復制機制
Docker鏡像由多個只讀層疊加而成,啟動容器時,Docker會加載只讀鏡像層并在鏡像棧頂部添加一個讀寫層。
如果運行中的容器修改了現有的一個已經存在的文件,那該文件將會從讀寫層下面的只讀層復制到讀寫層,該文件的只讀版本依然存在,只是已經被讀寫層中該文件的副本所隱藏,此即"寫時復制(COW)"機制。
?我們知道,容器在創建之后,實際上我們在容器中創建和修改的文件,實際上是被容器的分層機制保存在最頂層的容器層進行操作的,為了保護下面每一層的鏡像不被修改,所以才有了這樣的CopyOnWrite特性。
原因 | 結果 |
---|---|
Docker 使用 Copy-on-Write 機制 | 文件修改只發生在容器層,不影響鏡像層 |
容器層與容器生命周期綁定 | 容器銷毀時,容器層也被刪除 |
但是這樣也會導致容器在銷毀時數據的丟失,當我們銷毀容器重新創建一個新的容器時,所有的數據全部丟失,直接回到夢開始的地方。
在某些情況下,我們可能希望對容器內的某些文件進行持久化存儲,就需要使用到docker中提供給我們的持久化存儲幾種方式。
存儲方式 | 特點 | 是否推薦 |
---|---|---|
數據卷(Data Volume) | 由 Docker 管理,獨立于容器生命周期 | ? 推薦 |
數據卷容器(Data Volume Container) | 使用一個專門容器提供數據卷供其他容器掛載 | ? 舊版用法,現已被簡化 |
目錄掛載(綁定掛載) | 將宿主機目錄直接掛載到容器中 | ? 推薦 |
tmpfs 掛載 | 僅存在于內存中,重啟后丟失 | ? 不適合持久化 |
二:數據卷(Named Volume)
數據卷是 Docker 提供的一種持久化存儲機制,它獨立于容器的生命周期存在。
這意味著即使刪除了容器,只要不刪除數據卷,其中的數據依然可以被新創建的容器使用。
數據卷的特點
-
獨立性:數據卷與容器分離,不受容器生命周期影響。
-
共享性:支持在多個容器之間共享數據。
-
高效性:繞過聯合文件系統(UnionFS),提供更快的讀寫速度。
-
管理便捷:通過 Docker 命令直接管理,簡化操作流程。
數據卷的位置
默認情況下,Docker 將數據卷存儲在主機的 /var/lib/docker/volumes/
目錄下。
每個數據卷都有一個唯一的名稱和對應的目錄結構。
使用場景:
-
存儲數據庫數據(如 MySQL、PostgreSQL)
-
存放日志文件
-
開發環境中映射源碼目錄(雖然更推薦 bind mount)
-
在多個服務之間共享配置文件或緩存
1、數據卷操作命令
創建數據卷
docker volume create my_volume
這會創建一個名為 my_volume
的數據卷。
查看已有數據卷
docker volume ls
輸出示例:
DRIVER VOLUME NAME
local my_volume
local mysql_data
查看數據卷詳細信息
[root@localhost volumes]# docker volume inspect my_volume
[{"CreatedAt": "2025-05-30T15:57:40+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/my_volume/_data","Name": "my_volume","Options": null,"Scope": "local"}
]
可以看到數據卷在宿主機上的實際路徑(一般為 /var/lib/docker/volumes/my_volume/_data
)。
刪除數據卷
docker volume rm my_volume
注意:如果還有容器在使用這個數據卷,必須先停止并刪除這些容器才能刪除數據卷。
2、使用方式
在運行容器時掛載數據卷
格式:
docker run -v <volume_name>:<container_path> ...
例如:
docker run -d \--name n1 \-v v1:/usr/share/nginx/html \-p 80:80 \nginx
這條命令表示:
創建一個名為 nginx_html
的數據卷
如果指定的 volume 不存在:
Docker 會自動為我們 創建一個新的命名數據卷(Named Volume)
名字就是我們寫的那個
nginx_html
然后把這個新創建的數據卷掛載到容器中的指定路
將其掛載到容器的 /usr/share/nginx/html
路徑下
?
我掛載了一個空的數據卷(volume)到容器的某個目錄(如
/usr/share/nginx/html
),請問容器中的/usr/share/nginx/html
是否會被清空?我運行了 docker run -v my_volume:/path/in/container ...
my_volume
是一個 新創建的空數據卷容器鏡像在
/path/in/container
這個位置原本有文件那么 Docker 會自動把鏡像中原路徑下的內容復制到數據卷中,然后才掛載這個數據卷進容器。
這是 Docker 的一種保護機制,目的是:
防止用戶誤操作導致服務啟動失敗。
如果直接掛載一個空卷,而容器期望某些配置或靜態資源存在,會導致容器啟動后服務不可用。
所以 Docker 在第一次掛載空卷時,會自動將鏡像中原有的內容“遷移”過去,確保服務能正常運行。
如果我們不希望容器原有的內容被復制進來,可以這樣做:
方法一:手動初始化數據卷
提前準備好我們自己的內容放入數據卷,這樣就不會觸發自動復制。
方法二:使用綁定掛載(Bind Mount)
如果我們希望完全控制內容,可以使用綁定掛載代替命名卷
?掛載多個數據卷
docker run -d \--name mynginx \-p 80:80 \-v nginx_html:/usr/share/nginx/html \-v nginx_conf:/etc/nginx/conf.d \-v nginx_logs:/var/log/nginx \nginx
3、Nginx + 數據卷
步驟 1:創建并啟動 Nginx 容器
docker run -d \--name mynginx \-v nginx_html:/usr/share/nginx/html \-p 80:80 \nginx
步驟 2:向數據卷中添加網頁內容
# 創建測試文件
echo "Hello from Data Volume" > index.html# 將文件復制進容器(自動寫入數據卷)
docker cp index.html mynginx:/usr/share/nginx/html/
訪問瀏覽器:http://192.168.66.146/
,應能看到頁面顯示“Hello from Data Volume”。
步驟 3:刪除容器并重建
docker stop mynginx && docker rm mynginxdocker run -d \--name mynginx_new \-v nginx_html:/usr/share/nginx/html \-p 80:80 \nginx
4、MySQL + 數據卷
步驟 1:創建并啟動 MySQL 容器
docker run -d \--name mysql_db \-v mysql_data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=123456 \mysql:8.0.22
步驟 2:初始化數據庫
docker exec -it mysql_db bashmysql -uroot -p123456CREATE DATABASE testdb;
USE testdb;
CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100));
INSERT INTO users (name) VALUES ('Alice'), ('Bob');
步驟 3:刪除容器并重建
docker stop mysql_db && docker rm mysql_dbdocker run -d \--name mysql_db_new \-v mysql_data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=123456 \mysql:8.0.22
進入新容器查詢數據:
方式 | 類比 |
---|---|
docker exec -it xxx bash | 打開電腦 → 登錄系統 → 手動輸入命令 |
docker exec -it xxx 命令 | 直接遠程執行某個程序,不需要登錄系統 |
docker exec -it mysql_db_new mysql -uroot -p123456 -e "SELECT * FROM testdb.users;"
?我們應該看到之前插入的數據:“Alice” 和 “Bob” —— 數據依然存在!
5、總結
操作 | 命令 | 說明 |
---|---|---|
創建數據卷 | docker volume create myvol | 創建一個名為 myvol 的數據卷 |
列出數據卷 | docker volume ls | 查看所有已存在的數據卷 |
查看數據卷詳情 | docker volume inspect myvol | 查看數據卷的元數據及宿主機路徑 |
刪除數據卷 | docker volume rm myvol | 刪除指定數據卷(需先刪除使用它的容器) |
掛載數據卷到容器 | -v myvol:/path/in/container | 在運行容器時掛載數據卷 |
多個數據卷掛載 | -v vol1:/path1 -v vol2:/path2 | 支持同時掛載多個數據卷 |
創建一個名為
app_logs
的數據卷,并掛載到容器的/app/logs
路徑下。向數據卷中寫入日志文件,然后刪除容器再重建,驗證日志是否還在。
嘗試使用兩個不同的容器掛載同一個數據卷,實現數據共享。 ?
三:目錄掛載(Bind Mounts)
?
Bind mounts
允許你將宿主機上的一個文件或目錄直接掛載到容器中。
它不同于命名數據卷,因為它直接指向宿主機上的具體路徑。
特點:
-
靈活性高:可以指定任意宿主機路徑
-
實時同步:對掛載目錄的任何更改都會立即反映在容器和宿主機之間
-
適合開發環境:便于快速迭代代碼或配置文件
與 Named Volumes 對比:
卷 |Docker 文檔
數據卷(Named Volumes):是 Docker 內部管理的一種持久化存儲機制,獨立于容器的生命周期存在。它通常位于 /var/lib/docker/volumes/
下,并且由 Docker 自動管理。
目錄掛載(Bind Mounts):允許你將宿主機上的任意文件或目錄直接掛載到容器中。這意味著你可以指定具體的宿主機路徑,而不是讓 Docker 自動管理路徑。
數據卷的優勢
-
自動化管理:由 Docker 自動管理路徑和生命周期,減少了手動干預。
-
更好的隔離性:適合生產環境下的多用戶或多應用部署。
-
更高的性能:繞過了聯合文件系統,提供了更快的讀寫速度。
目錄掛載的優勢
-
靈活性:能夠直接操作宿主機文件系統,適合開發調試和特定路徑需求。
-
實時更新:任何對宿主機文件的修改都會即時反映在容器內,非常適合快速迭代。
-
精確控制:可以將容器內的目錄映射到宿主機上的任意位置,滿足復雜的部署需求。
1、基本操作
掛載目錄到容器:
-v
這是較早的方式,語法相對簡單直接當你指定一個宿主機路徑(如
/home/user/data
)時,它會創建一個綁定掛載(bind mount)。如果只提供一個名稱(如
my_volume
),則 Docker 會將其視為一個命名卷
docker run -v /host/path:/container/path ...
--mount
選項這是更新的方式,提供了更清晰、更詳細的語法,并且支持更多選項
docker run --mount type=bind,source=<host_path>,target=<container_path> ...
或者用于命名卷:
docker run --mount type=volume,source=my_volume,target=/path/in/container ...
-
type
可以是bind
或volume
,分別表示綁定掛載和命名卷。 -
source
或src
指定宿主機上的源路徑(對于綁定掛載)或卷名(對于命名卷)。 -
target
或dst
指定容器內的目標路徑。
優點:
-
更加明確,尤其是在處理復雜的掛載需求時(例如設置只讀權限、傳播模式等)。
-
支持更多的高級功能,比如 SELinux 標簽、只讀模式等。
使用 -v
創建綁定掛載:
docker run -d \--name mynginx \-v $(pwd)/html:/usr/share/nginx/html \nginx
使用 --mount
創建相同的綁定掛載:
docker run -d \--name mynginx \--mount type=bind,source=$(pwd)/html,target=/usr/share/nginx/html \nginx
使用 -v
創建命名卷:
docker run -d \--name mynginx \-v nginx_html:/usr/share/nginx/html \nginx
使用 --mount
創建相同的命名卷:
docker run -d \--name mynginx \--mount type=volume,source=nginx_html,target=/usr/share/nginx/html \nginx
參數名 | 含義 | 示例 |
---|---|---|
type | 掛載類型,這里是 bind | type=bind |
source 或 src | 宿主機上要掛載的文件或目錄路徑 | source=/home/user/html |
target 或 dst | 容器內掛載的目標路徑 | target=/usr/share/nginx/html |
readonly | 設置為只讀模式(可選) | 添加 readonly 表示只讀 |
掛載多個目錄
docker run -d \--name mynginx \--mount type=bind,source=$(pwd)/html,target=/usr/share/nginx/html \--mount type=bind,source=$(pwd)/conf.d,target=/etc/nginx/conf.d \--mount type=bind,source=$(pwd)/logs,target=/var/log/nginx \-p 80:80 \nginx
適用于需要多目錄掛載的復雜服務。
設置只讀掛載
docker run -d \--name mynginx \--mount type=bind,source=$(pwd)/html,target=/usr/share/nginx/html,readonly \-p 80:80 \nginx
-
容器內的
/usr/share/nginx/html
目錄變為只讀 -
防止容器程序意外修改宿主機文件
2、Nginx + 目錄掛載
創建并啟動容器
mkdir -p html
echo "Hello from Bind Mount" > html/index.htmldocker run -d \--name mynginx \--mount type=bind,source=$(pwd)/html,target=/usr/share/nginx/html \-p 80:80 \nginx
驗證頁面內容
訪問瀏覽器:http://localhost/
應能看到 “Hello from Bind Mount”
修改宿主機文件并驗證同步
編輯 html/index.html
,例如改為:
<h1>Hello again from Bind Mount</h1>
刷新瀏覽器頁面,應能看到更新內容。
刪除容器并重建
docker stop mynginx && docker rm mynginxdocker run -d \--name mynginx_new \--mount type=bind,source=$(pwd)/html,target=/usr/share/nginx/html \-p 80:80 \nginx
再次訪問頁面,內容依然存在。
3、MySQL + 目錄掛載
步驟 1:創建并啟動容器
mkdir -p mysql_datadocker run -d \--name mysql_db \--mount type=bind,source=$(pwd)/mysql_data,target=/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=123456 \mysql:8.0.22
步驟 2:初始化數據庫
docker exec -it mysql_db bash
mysql -uroot -p123456CREATE DATABASE testdb;
USE testdb;CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100)
);INSERT INTO users (name) VALUES ('Alice'), ('Bob');
步驟 3:刪除容器并重建
docker stop mysql_db && docker rm mysql_dbdocker run -d \--name mysql_db_new \--mount type=bind,source=$(pwd)/mysql_data,target=/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=123456 \mysql:8.0.22
查詢驗證數據是否保留:
docker exec -it mysql_db_new mysql -uroot -p123456 -e "SELECT * FROM testdb.users;"
4、靜態網頁服務 + 實時更新
場景描述:
你是一個運維人員,負責部署一個簡單的靜態網站。你想把網頁文件放在宿主機上,并隨時更新它們,而不需要每次重啟容器。
步驟 1:創建本地網頁目錄
mkdir -p /opt/myweb?
cd /opt/myweb?
echo "<h1>Welcome to My Website</h1>" > index.html
步驟 2:啟動容器并掛載網頁目錄
docker run -d \ ?
--name myhttpd \ ?
--mount type=bind,source=/opt/myweb,target=/usr/local/apache2/htdocs \ ?
-p 80:80 \
httpd
Apache 默認的網頁根目錄是
/usr/local/apache2/htdocs
步驟 3:訪問頁面驗證
打開瀏覽器訪問:
http://localhost/
應能看到 “Welcome to My Website”
步驟 4:修改網頁內容并驗證同步
編輯宿主機上的網頁:
echo "<h1>Hello from Bind Mount</h1>" > /opt/myweb/index.html
刷新瀏覽器,應能看到新內容!
Apache HTTPD 默認監聽端口是 80
,它的監聽端口是在配置文件中定義的,通常是
`/usr/local/apache2/conf/httpd.conf`。
創建本地目錄并準備配置文件
mkdir -p /opt/httpd-conf/conf/
你可以先從一個正在運行的 Apache 容器中復制出默認的配置,再修改它。
創建一個臨時容器:
docker run -d --name temp_httpd httpd
復制默認配置到宿主機:
docker cp temp_httpd:/usr/local/apache2/conf /opt/httpd-conf
停止并刪除臨時容器:
docker stop temp_httpd && docker rm temp_httpd
現在 /opt/httpd-conf
中就包含了完整的 Apache 配置文件。
修改 httpd.conf
設置監聽端口為 8080
編輯配置文件:
vim /opt/httpd-conf/conf/httpd.conf
找到這一行:
Listen 80
改為:
Listen 8080
保存退出。
啟動容器時掛載配置文件目錄
docker run -d \--name myhttpd \--mount type=bind,source=/opt/httpd-conf/conf/,target=/usr/local/apache2/conf \-p 8080:8080 \httpd
使用 --mount
掛載單個文件
基本語法
docker run --mount type=bind,source=<宿主機文件路徑>,target=<容器內文件路徑> <鏡像名>
?
-
type=bind
:指定掛載類型為綁定掛載。 -
source
或src
:宿主機上的源文件路徑。 -
target
或dst
:容器內的目標文件路徑。
掛載單個配置文件到 Apache HTTPD 容器中
假設我們有一個自定義的 httpd.conf
文件,并希望將其掛載到運行中的 Apache HTTPD 容器中以替換默認的配置文件。
使用以下命令啟動 Apache HTTPD 容器,并通過 --mount
將宿主機上的單個配置文件掛載到容器內的相應位置:
docker run -d \--name myhttpd \--mount type=bind,source=/opt/httpd-conf/conf/httpd.conf,target=/usr/local/apache2/conf/httpd.conf \-p 8080:8080 \httpd
我們掛載了 /opt/httpd-conf/httpd.conf
到容器內的 /usr/local/apache2/conf/httpd.conf
路徑。
這樣做的結果是,Apache HTTPD 在容器中會使用你提供的配置文件,而不是默認的那個。
5、日志共享 + 多容器集中查看
場景描述:
你需要讓多個 Web 服務容器的日志都輸出到同一個目錄,方便統一管理和分析。
編輯配置文件,加入
CustomLog "logs/access_log" common
ErrorLog "logs/error_log"
步驟 1:創建共享日志目錄
mkdir -p /data/logs/weblogs
步驟 2:啟動第一個 Apache 容器
# 容器 web1 掛載 log1 子目錄
docker run -d \--name web1 \--mount type=bind,source=/data/logs/weblogs/log1,target=/usr/local/apache2/logs \--mount type=bind,source=/opt/httpd-conf/conf/httpd.conf,target=/usr/local/apache2/conf/httpd.conf \-p 80:80 \httpd
步驟 3:啟動第二個 Apache 容器
# 容器 web2 掛載 log2 子目錄
docker run -d \--name web2 \--mount type=bind,source=/data/logs/weblogs/log2,target=/usr/local/apache2/logs \--mount type=bind,source=/opt/httpd-conf/conf/httpd.conf,target=/usr/local/apache2/conf/httpd.conf \-p 8080:80 \httpd
/data/logs/weblogs/
├── log1/
│ ├── access_log
│ └── error_log
├── log2/
│ ├── access_log
│ └── error_log
步驟 4:查看共享日志
ls /data/logs/weblogstail -f /data/logs/weblogs/access_log