使⽤端⼝轉發解決容器端⼝訪問問題
1、MySQL應⽤端⼝轉發
-p
創建應⽤容器的時候,⼀般會做端⼝映射,這樣是為了讓外部 能夠訪問這些容器⾥的應⽤。可以⽤多個-p指定多個端⼝映射關 系。
本例使⽤-p把本地3307轉發到容器的3306,其他參數需要查看發 布容器的⻚⾯提示
查看本地地址:
[root@qfedu.com ~]#ip a
ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:0a:5b:8b brd ff:ff:ff:ff:ff:ff
inet 192.168.245.134/24 brd 192.168.245.255 scope
global dynamic ens33
valid_lft 1444sec preferred_lft 1444sec
運⾏容器:
[root@qfedu.com ~]# docker run --name mysql1 -p
3307:3306 -e MYSQL_ROOT_PASSWORD=123
daocloud.io/library/mysql
查看Ip地址:
[root@qfedu.com ~]# docker inspect mysql1 | grep
IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",
通過本地IP:192.168.245.134的3307端⼝訪問容器mysql1內的數
據庫,出現如下提示恭喜你
[root@qfedu.com ~]# mysql -u root -p123 -h
192.168.245.134 -P3307
Welcome to the MariaDB monitor. Commands end with ;
or \g.
Your MySQL connection id is 3
Server version: 5.7.18 MySQL Community Server (GPL)
Copyright (c) 2000, 2016, Oracle, MariaDB
Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear
the current input statement.
MySQL [(none)]>
2、Redis應⽤端⼝轉發
-P
Docker 會隨機映射⼀個 49000~49900 的端⼝到內部容器開 放的⽹絡端⼝。如下:
[root@qfedu.com ~]# docker images
REPOSITORY TAG IMAGE ID CREATED
SIZE
docker.io/redis latest e4a35914679d 2
weeks ago 182.9 MB
[root@qfedu.com ~]# docker run --name myredis -P -d
docker.io/redis
805d0e21e531885aad61d3e82395210b50621f1991ec4b7f9a0e
25c815cc0272
[root@qfedu.com ~]# docker ps
CONTAINER ID IMAGE COMMAND
CREATED STATUS PORTS NAMES
805d0e21e531 docker.io/redis "dockerentrypoint.sh" 4 seconds ago Up 3 seconds
0.0.0.0:32768->6379/tcp myredis
從上⾯的結果中可以看出,本地主機的32768端⼝被映射到了redis容
器的6379端⼝上,也就是說訪問本機的32768
端⼝即可訪問容器內redis端⼝。
測試看下,登陸redis容器,隨意寫個數據
[root@qfedu.com ~]# docker run --rm -it --name
myredis2 --link myredis:redisdb docker.io/redis
/bin/
bash
root@be44d955d6f4:/data[root@qfedu.com ~]# redis-cli
-h redisdb -p 6379
redisdb:6379> set wing 123
OK
redisdb:6379>
在別的機器上通過上⾯映射的端⼝32768連接這個容器的redis
[root@qfedu.com ~]# redis-cli -h 192.168.245.134 -p
32768
192.168.1.23:32768> get wing
"123"
1、容器卷操作
容器卷是容器和宿主機之間的⽂件共享⽅式之⼀
新卷只能在容器創建過程當中掛載
[root@qfedu.com ~]# docker run -it --name="voltest"
-v /tmp:/test daocloud.io/library/centos:5
/bin/bash
共享其他容器的卷:
[root@qfedu.com ~]# docker run -it --volumes-from
bc4181 daocloud.io/library/centos:5 /bin/bash
實際應⽤中可以利⽤多個-v選項把宿主機上的多個⽬錄同時共享給新建
容器:
⽐如:
[root@qfedu.com ~]# docker run -it -v /abc:/abc -v
/def:/def 1ae9
[root@qfedu.com ~]# docker run -v
/vol/index.html:/usr/share/nginx/html/index.html -it
nginx /bin/bash
注意:
如果是⽂件共享,數據不能同步更新
2、Volume【擴展閱讀】
容器技術使⽤了 rootfs 機制和 Mount Namespace,構建出了⼀ 個同宿主機完全隔離開的⽂件系統環境。這時候,就需要考慮這樣兩個 問題:
容器⾥進程新建的⽂件,怎麼才能讓宿主機獲取到?
宿主機上的⽂件和⽬錄,怎麼才能讓容器⾥的進程訪問到?
這正是 Docker Volume 要解決的問題:Volume 機制,允許你將 宿主機上指定的⽬錄或者⽂件,掛載到容器⾥⾯進⾏讀取和修改操作。
在 Docker 項⽬⾥,它⽀持兩種 Volume 聲明⽅式,可以把宿主機 ⽬錄掛載進容器的 /test ⽬錄當中:
[root@qfedu.com ~]# docker run -v /test ...
[root@qfedu.com ~]# docker run -v /home:/test ...
這兩種聲明⽅式的本質是相同的:都是把⼀個宿主機的⽬錄掛載進 了容器的 /test ⽬錄。
第⼀種情況沒有顯示聲明宿主機⽬錄,Docker 就會默認在宿主機 上創建⼀個臨時⽬錄 /var/lib/docker/volumes/[VOLUME_ID]/_data, 然後把它掛載到容器的 /test ⽬錄上。
第⼆種情況,Docker 就直接把宿主機的 /home ⽬錄掛載到容器 的 /test ⽬錄上。
那麼,Docker ⼜是如何做到把⼀個宿主機上的⽬錄或者⽂件,掛 載到容器⾥⾯去呢?難道⼜是 Mount Namespace 的⿊科技嗎?
實際上,並不需要這麼麻煩。
已經介紹過,當容器進程被創建之後,儘管開啟了 Mount Namespace,但是在它執⾏ chroot(或者 pivot_root)之前,容器進 程⼀直可以看到宿主機上的整個⽂件系統。
⽽宿主機上的⽂件系統,也⾃然包括了要使⽤的容器鏡像。這個鏡 像的各個層,保存在 /var/lib/docker/aufs/diff ⽬錄下,在容器進程啟 動後,它們會被聯合掛載在 /var/lib/docker/aufs/mnt/ ⽬錄中,這樣 容器所需的 rootfs 就準備好了。
所以,只需要在 rootfs 準備好之後,在執⾏ chroot 之前,把 Volume 指定的宿主機⽬錄(⽐如 /home ⽬錄),掛載到指定的容器 ⽬錄(⽐如 /test ⽬錄)在宿主機上對應的⽬錄(即 /var/lib/docker/aufs/mnt/[可讀寫層 ID]/test)上,這個 Volume 的掛 載⼯作就完成了。
由於執⾏這個掛載操作時,"容器進程"已經創建了,也就意味著此 時 Mount Namespace 已經開啟了。所以,這個掛載事件只在這個容 器⾥可⻅。你在宿主機上,是看不⻅容器內部的這個掛載點的。這就保 證了容器的隔離性不會被 Volume 打破。
注意:這⾥提到的 " 容器進程 ",是 Docker 創建的⼀個容器初始 化進程 (dockerinit),⽽不是應⽤進程 (ENTRYPOINT + CMD)。dockerinit 會負責完成根⽬錄的準備、掛載設備和⽬錄、配置 hostname 等⼀系列需要在容器內進⾏的初始化操作。最後,它通過 execv() 系統調⽤,讓應⽤進程取代⾃⼰,成為容器⾥的 PID=1 的進 程。
⽽這⾥要使⽤到的掛載技術,就是 Linux 的綁定掛載(bind mount)機制。它的主要作⽤就是,允許你將⼀個⽬錄或者⽂件,⽽ 不是整個設備,掛載到⼀個指定的⽬錄上。並且,這時你在該掛載點上 進⾏的任何操作,只是發⽣在被掛載的⽬錄或者⽂件上,⽽原掛載點的 內容則會被隱藏起來且不受影響。
其實,如果你了解 Linux 內核的話,就會明⽩,綁定掛載實際上是 ⼀個 inode 替換的過程。在 Linux 作業系統中,inode 可以理解為存 放⽂件內容的"對象",⽽ dentry,也叫⽬錄項,就是訪問這個 inode 所使⽤的"指針"
mount --bind /home /test,會將 /home 掛載到 /test 上。其實相 當於將 /test 的 dentry,重定向到了 /home 的 inode。這樣當修改 /test ⽬錄時,實際修改的是 /home ⽬錄的 inode。這也就是為何,⼀ 旦執⾏ umount 命令,/test ⽬錄原先的內容就會恢復:因為修改真正發⽣在的,是 /home ⽬錄⾥。
進程在容器⾥對這個 /test ⽬錄進⾏的所有操作,都實際發⽣在宿 主機的對應⽬錄(⽐如,/home,或者 /var/lib/docker/volumes/[VOLUME_ID]/_data)⾥,⽽不會影響容器 鏡像的內容。
這個 /test ⽬錄⾥的內容,既然掛載在容器 rootfs 的可讀寫層,它 會不會被 docker commit 提交掉呢?
也不會。
原因前⾯提到過。容器的鏡像操作,⽐如 docker commit,都是 發⽣在宿主機空間的。⽽由於 Mount Namespace 的隔離作⽤,宿主 機並不知道這個綁定掛載的存在。所以,在宿主機看來,容器中可讀寫 層的 /test ⽬錄(/var/lib/docker/aufs/mnt/[可讀寫層 ID]/test),始 終是空的。
不過,由於 Docker ⼀開始還是要創建 /test 這個⽬錄作為掛載 點,所以執⾏了 docker commit 之後,新產⽣的鏡像⾥,會多出來⼀ 個空的 /test ⽬錄。畢竟,新建⽬錄操作,⼜不是掛載操作,Mount Namespace 對它可起不到"障眼法"的作⽤。
1.啟動⼀個 helloworld 容器,給它聲明⼀個 Volume,掛載在容
器⾥的 /test ⽬錄上:
[root@qfedu.com ~]# docker run -d -v /test
helloworld
cf53b766fa6f
2.容器啟動之後,查看⼀下這個 Volume 的 ID:
[root@qfedu.com ~]# docker volume ls
DRIVER VOLUME NAME
local
cb1c2f7221fa9b0971cc35f68aa1034824755ac44a034c0c0a1d
d318838d3a6d
3.使⽤這個 ID,可以找到它在 Docker ⼯作⽬錄下的 volumes 路
徑:
[root@qfedu.com ~]# ls
/var/lib/docker/volumes/cb1c2f7221fa/_data/
這個 _data ⽂件夾,就是這個容器的 Volume 在宿主機上對應的臨
時⽬錄了。
4.在容器的 Volume ⾥,添加⼀個⽂件 text.txt:
[root@qfedu.com ~]# docker exec -it cf53b766fa6f
/bin/sh
cd test/
touch text.txt
5.再回到宿主機,就會發現 text.txt 已經出現在了宿主機上對應的
臨時⽬錄⾥:
[root@qfedu.com ~]# ls
/var/lib/docker/volumes/cb1c2f7221fa/_data/
text.txt
可是,如果你在宿主機上查看該容器的可讀寫層,雖然可以看到這個
/test ⽬錄,但其內容是空的:
[root@qfedu.com ~]# ls
/var/lib/docker/aufs/mnt/6780d0778b8a/test
可以確認,容器 Volume ⾥的信息,並不會被 docker commit 提 交掉;但這個掛載點⽬錄 /test 本身,則會出現在新的鏡像當中。以上 內容,就是 Docker Volume 的核⼼原理了。
Docker 容器"全景圖":
⼀個"容器",實際上是⼀個由 Linux Namespace、Linux Cgroups 和 rootfs 三種技術構建出來的進程的隔離環境。