成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

五種方式:構(gòu)建小巧Docker容器的學(xué)問

譯文
云計(jì)算
幾年之前,Docker的爆炸式發(fā)展將容器與容器鏡像概念引入了大眾視野。盡管之前已經(jīng)存在Linux容器,但Docker憑借著用戶友好的命令行界面以及易于理解的Dockerfile格式顯著降低了鏡像的構(gòu)建門檻。在本文中,我們將共同了解五種優(yōu)化Linux容器大小并構(gòu)建小巧鏡像的方法。

【51CTO.com快譯】在本文中,我們將共同了解五種優(yōu)化Linux容器大小并構(gòu)建小巧鏡像的方法。

幾年之前,Docker的爆炸式發(fā)展將容器與容器鏡像概念引入了大眾視野。盡管之前已經(jīng)存在Linux容器,但Docker憑借著用戶友好的命令行界面以及易于理解的Dockerfile格式顯著降低了鏡像的構(gòu)建門檻。但必須承認(rèn)的是,盡管上手難度已經(jīng)有所下降,其中仍存在著一些細(xì)微的差別與技巧,能夠幫助我們構(gòu)建功能強(qiáng)大但卻體積小巧的容器鏡像。

[[237837]]

第一關(guān):清理內(nèi)容

下面列舉的部分示例采取與傳統(tǒng)服務(wù)器類似的清理方式,只是具體要求更為嚴(yán)格。鏡像的體積對(duì)于快速移動(dòng)而言至關(guān)重要,而且在磁盤之上存儲(chǔ)多套不必要的數(shù)據(jù)副本無(wú)疑將浪費(fèi)大量資源。因此,我們有必要盡可能利用技術(shù)控制容器鏡像的“身材”。

下面來(lái)看如何從鏡像中刪除緩存文件,從而節(jié)約存儲(chǔ)空間。首先利用dnf以包含及不包含元數(shù)據(jù)的方式安裝Nginx,查看二者之間的鏡像大小區(qū)別; 而后利用yum進(jìn)行緩存清理:

 

  1. # Dockerfile with cache 
  2. FROM fedora:28 
  3. LABEL maintainer Chris Collins <collins.christopher@gmail.com> 
  4.  
  5. RUN dnf install -y nginx 
  6.  
  7. ----- 
  8.  
  9. # Dockerfile w/o cache 
  10. FROM fedora:28 
  11. LABEL maintainer Chris Collins <collins.christopher@gmail.com> 
  12.  
  13. RUN dnf install -y nginx \ 
  14.         && dnf clean all \ 
  15.         && rm -rf /var/cache/yum 
  16.  
  17. ----- 
  18.  
  19. [chris@krang] $ docker build -t cache -f Dockerfile .   
  20. [chris@krang] $ docker images --format "{{.Repository}}: {{.Size}}"  
  21. | head -n 1 
  22. cache: 464 MB 
  23.  
  24. [chris@krang] $ docker build -t no-cache -f Dockerfile-wo-cache . 
  25. [chris@krang] $ docker images --format "{{.Repository}}: {{.Size}}"  | head -n 1 
  26. no-cache: 271 MB 

可以看到,二者之間的體積存在顯著差異。包含dnf緩存的版本幾乎是不包含元數(shù)據(jù)及緩存的鏡像大小的兩倍。事實(shí)上,工具包管理器緩存、Ruby gem臨時(shí)文件、nodejs緩存、甚至是已下載的源代碼壓縮包都是清理工作的主要對(duì)象。

分層——一個(gè)潛在問題

遺憾的是(或者可以說(shuō)幸運(yùn)的是,具體如后文所述),由于容器以分層方式使用,因此大家無(wú)法簡(jiǎn)單將RUN rm -rf /var/cache/yum 添加到Dockerfile當(dāng)中并就此作罷。Dockerfile中的每條指令都存儲(chǔ)在一個(gè)層中,各層之間的變更最終應(yīng)用于頂層。所以即使您進(jìn)行如下操作:

 

  1. RUN dnf install -y nginx 
  2. RUN dnf clean all 
  3. RUN rm -rf /var/cache/yum 

……最終仍會(huì)得到三層,其中一層包含所有緩存,兩個(gè)中間層則從鏡像中“移除”緩存。然而,緩存仍然實(shí)際存在,正如當(dāng)您將某一文件系統(tǒng)安裝在另一文件系統(tǒng)之上時(shí),文件就在這里——只是我們無(wú)法查看或者訪問。

需要注意的是,上一節(jié)中的示例將緩存清理鏈接到了生存緩存的同一Dockerfile指令當(dāng)中:

 

  1. RUN dnf install -y nginx \ 
  2.         && dnf clean all \ 
  3.         && rm -rf /var/cache/yum 

這是一條單獨(dú)指令,最終會(huì)成為鏡像中的一層。通過這種方式,您會(huì)丟棄一部分Docker緩存——這意味著鏡像重構(gòu)時(shí)間會(huì)稍長(zhǎng),但緩存數(shù)據(jù)仍將出現(xiàn)在最終鏡像當(dāng)中。作為一種良好的折衷方案,我們只需鏈接相關(guān)命令(例如hum install與hum clean all,或者下載、釋放及移除源tarball等)即可幫助最終鏡像顯著瘦身,同時(shí)繼續(xù)利用Docker緩存加快開發(fā)速度。

然而,這里的層將比前文中提到的更加微妙。因?yàn)殓R像各層記錄了每個(gè)層的具體變化——因此除了添加的文件之外,一切文件修改都將被納入其中。例如,即使更改了文件模式,鏡像中也會(huì)有新層出現(xiàn)以創(chuàng)建該文件的副本。

舉例來(lái)說(shuō),以下docker images輸出結(jié)果顯示出與兩套鏡像相關(guān)的信息。第一套layer_test_1通過將單一1 GB文件添加至基礎(chǔ)CentOS鏡像的方式得出。第二套鏡像layer_test_2則直接由layer_test_1創(chuàng)建而來(lái),只是利用chmod u+x命令變更了該1 GB文件的模式。

 

  1. layer_test_2        latest       e11b5e58e2fc           7 seconds ago           2.35 GB 
  2. layer_test_1        latest       6eca792a4ebe           2 minutes ago           1.27 GB 

如大家所見,新的鏡像較前一套鏡像大出1 GB有余。盡管layer_test_1 實(shí)際上只代表著layer_test_2的前兩層,但第二套鏡像中仍然隱藏著另一個(gè)1 GB的文件。在鏡像構(gòu)建過程當(dāng)中,一切與文件相關(guān)的刪除、移除或更改都會(huì)造成這樣的結(jié)果。

專用鏡像與靈活鏡像

一則軼事:當(dāng)初我們大量采用Ruby on Rails應(yīng)用程序時(shí),同事們開始慢慢接受容器這種新鮮事物。我們的第一項(xiàng)工作就是為所有團(tuán)隊(duì)創(chuàng)建一套官方的Ruby基礎(chǔ)鏡像。為了簡(jiǎn)單起見,我們利用rebenv將四套最新的Ruby版本安裝到了鏡像當(dāng)中,從而允許我們的開發(fā)人員能夠利用單一版本將所有應(yīng)用程序遷移到容器鏡像當(dāng)中。這實(shí)際上帶來(lái)了一套非常龐大但卻比較靈活(至少我們認(rèn)為)的鏡像,其中涵蓋我們各合作團(tuán)隊(duì)間的一切工作基礎(chǔ)。

但事實(shí)證明,這一切都是在浪費(fèi)時(shí)間。維護(hù)特定鏡像的單一修改版本能夠比較輕松地實(shí)現(xiàn)自動(dòng)化,這是因?yàn)闉樘囟ㄧR像選擇特定版本實(shí)際上有助于在引入突破性變更之前意識(shí)到原有應(yīng)用程序已經(jīng)不合適接下來(lái)的需求,從而避免由此發(fā)生嚴(yán)重破壞。此外,過大的鏡像也造成了資源浪費(fèi):當(dāng)我們對(duì)不同Ruby版本進(jìn)行拆分時(shí),我們最終得到了多套共享同一基礎(chǔ)的鏡像。如果將其同時(shí)保存在服務(wù)器之上,相較于包含多個(gè)版本的巨型鏡像,其占用的額外空間其實(shí)并不大,但傳輸速度卻要快得多。

這并不是說(shuō)構(gòu)建靈活性鏡像沒有意義。只是在我們的情況下,創(chuàng)建專用型鏡像最終節(jié)約了存儲(chǔ)空間與維護(hù)時(shí)間,同時(shí)也確保各團(tuán)隊(duì)在享受好處的同時(shí)能夠?qū)灿谢A(chǔ)鏡像做出必要的修改。

從頭開始:將需要的內(nèi)容添加至空白鏡像中

與Dockerfile的用戶友好與易用性類似,還有其他一些工具能夠以極為靈活的方式創(chuàng)建小巧的Docker兼容容器鏡像且無(wú)需完整的操作系統(tǒng)——其小巧程度甚至堪比標(biāo)準(zhǔn)Docker基礎(chǔ)鏡像。

我在之前曾經(jīng)寫過關(guān)于Buildah的文章,這里我也會(huì)再次提及,因?yàn)槠湎喈?dāng)靈活且可利用主機(jī)中的工具從零開始創(chuàng)建鏡像,同時(shí)安裝打包軟件并修改鏡像內(nèi)容。更重要的是,這些工具將永遠(yuǎn)存在于鏡像之外,因此不會(huì)增加鏡像本身的體積。

Buildah取代了docker build命令。有了它,您可以將容器鏡像的文件系統(tǒng)掛載至主機(jī)上,并利用主機(jī)中的工具與其進(jìn)行交互。

讓我們嘗試?yán)蒙厦娴腘ginx示例看看Biuldah的效果(這里暫時(shí)不管緩存):

 

  1. #!/usr/bin/env bash 
  2. set -o errexit 
  3.  
  4. # 創(chuàng)建一個(gè)容器 
  5. container=$(buildah from scratch) 
  6.  
  7. # 掛載容器文件系統(tǒng) 
  8. mountpoint=$(buildah mount $container) 
  9.  
  10. # 安裝一個(gè)基礎(chǔ)文件系統(tǒng)與最低軟件包集,以及nginx 
  11. dnf install --installroot $mountpoint  --releasever 28 glibc-minimal-langpack nginx --setopt install_weak_deps=false -y 
  12.  
  13. # 將容器保存為鏡像 
  14. buildah commit --format docker $container nginx 
  15.  
  16. # 清理 
  17. buildah unmount $container 
  18.  
  19. # 將鏡像推著至Docker守護(hù)程序進(jìn)行存儲(chǔ) 
  20. buildah push nginx:latest docker-daemon:nginx:latest 

大家可能已經(jīng)注意到,這里我們不再使用Dockerfile構(gòu)建鏡像,而是使用簡(jiǎn)單的Bash腳本。我們利用一套從零創(chuàng)建(或空白)鏡像進(jìn)行構(gòu)建。該Bash腳本會(huì)將容器的root文件系統(tǒng)掛載至主機(jī)上的某個(gè)掛載點(diǎn),而后利用主機(jī)命令安裝各軟件包。通過這種方式,軟件包管理器甚至無(wú)需超出容器自身范圍。

如果沒有額外的部分——例如dnf等基礎(chǔ)鏡像中的額外內(nèi)容——那么鏡像本身的大小僅為304 MB,這一體積要比之前利用Dockerfile構(gòu)建的Nginx鏡像小上100多MB。

 

  1. [chris@krang] $ docker images |grep nginx 
  2. docker.io/nginx      buildah      2505d3597457    4 minutes ago         304 MB 

注意:鏡像名稱中之所以包含docker.io部分,是因?yàn)槠浔煌扑椭罝ocker守護(hù)程序的命名空間,但其仍然是利用以上構(gòu)建腳本以本地方式構(gòu)建的鏡像。

考慮到基礎(chǔ)鏡像本身只有300 MB左右,100 MB的節(jié)約幅度顯然相當(dāng)驚人。利用軟件管理器安裝Nginx,也會(huì)帶來(lái)大量的依賴關(guān)系。如果使用由主機(jī)提供的工具進(jìn)行源代碼編譯的處理方式,由于您可以選擇確切的依賴關(guān)系而非引入任何不必要的額外文件,大家將能夠進(jìn)一步節(jié)約存儲(chǔ)空間。

利用Buildah構(gòu)建鏡像能夠有效擺脫完整操作系統(tǒng)以及構(gòu)建工具,從而進(jìn)一步壓縮您的鏡像體積。而對(duì)于某些特定類型的鏡像,我們還可以采取同樣的方法創(chuàng)建出僅包含應(yīng)用程序本身的鏡像。

僅使用靜態(tài)鏈接的二進(jìn)制文件創(chuàng)建鏡像

遵循相同的理念,我們可以進(jìn)一步將管理與構(gòu)建工具從鏡像中清理出去。如果我們擁有必要的專業(yè)知識(shí),且不再需要立足容器內(nèi)部進(jìn)行故障排查,那么我們是否可以棄用Bash?我們還需要GNU核心程序嗎?我們還需要基礎(chǔ)的Linux文件系統(tǒng)嗎?大家可以使用任何編譯語(yǔ)言執(zhí)行此項(xiàng)操作,即利用靜態(tài)鏈接庫(kù)創(chuàng)建二進(jìn)制文件——程序運(yùn)行所需要的一切庫(kù)及函數(shù)都將被復(fù)制并存儲(chǔ)在二進(jìn)制文件當(dāng)中。

這是一種在Golang社區(qū)中擁有一定人氣的處理方式,因此我們這里使用Go應(yīng)用程序進(jìn)行演示。以下是Dockerfile采用一個(gè)小巧的Go Hello-World應(yīng)用程序,并將其編譯在一套FROM golang:1.8鏡像當(dāng)中:

 

  1. FROM golang:1.8 
  2.  
  3. ENV GOOS=linux 
  4. ENV appdir=/go/src/gohelloworld 
  5.  
  6. COPY ./ /go/src/goHelloWorld 
  7. WORKDIR /go/src/goHelloWorld 
  8.  
  9. RUN go get 
  10. RUN go build -o /goHelloWorld -a 
  11.  
  12. CMD ["/goHelloWorld"

最終得到的鏡像包含二進(jìn)制文件、源代碼以及基礎(chǔ)鏡像層,總體積為716 MB。但是,我們的應(yīng)用程序最終真正需要的只有編譯后的二進(jìn)制文件,其他所有內(nèi)容都是多余的。

如果我們?cè)诮y(tǒng)計(jì)時(shí)利用CGO_ENABLED=0禁用cgo,則可創(chuàng)建出一套不打包C庫(kù)的二進(jìn)制文件:

  1. GOOS=linux CGO_ENABLED=0 go build -a goHelloWorld.go 

生成的二進(jìn)制文件可被添加至空的,或者“從頭構(gòu)建”鏡像當(dāng)中:

 

  1. FROM scratch 
  2. COPY goHelloWorld / 
  3. CMD ["/goHelloWorld"

下面,我們來(lái)比較兩套鏡像之間的體積差異:

 

  1. [ chris@krang ] $ docker images 
  2. REPOSITORY      TAG             IMAGE ID                CREATED                 SIZE 
  3. goHello     scratch     a5881650d6e9            13 seconds ago          1.55 MB 
  4. goHello     builder     980290a100db            14 seconds ago          716 MB 

可以看到,差別非常巨大。由golang:1.8構(gòu)建出的鏡像中包含goHelloWorld庫(kù)(標(biāo)記為‘builder’),其體積達(dá)到純二進(jìn)制文件鏡像的460倍。而純二進(jìn)制文件鏡像的體積僅為1.55 MB。這意味著如果我們使用由builder構(gòu)建的鏡像,其中將有約713 MB的數(shù)據(jù)根本不必存在。

如果適合,不妨考慮壓縮方法

還有一種方法可以通過將所有命令鏈接至層內(nèi)以節(jié)約空間,就是鏡像壓縮(squash)。在進(jìn)行鏡像壓縮時(shí),您實(shí)際上是在導(dǎo)出鏡像,刪除所有中間層,并將鏡像的當(dāng)前狀態(tài)保存為單一層。這將有效控制鏡像的實(shí)際體積。

過去,我們需要利用一些創(chuàng)造性的解決方案才能將經(jīng)過壓縮的層進(jìn)行還原——例如導(dǎo)出容器內(nèi)容并將其重新導(dǎo)入為單層鏡像,或者利用docker-squash等工具。但從1.13版本開始,Docker引入了一種便利的標(biāo)記——squash,其能夠在構(gòu)建過程中完成同樣的操作:

 

  1. FROM fedora:28 
  2. LABEL maintainer Chris Collins <collins.christopher@gmail.com> 
  3.  
  4. RUN dnf install -y nginx 
  5. RUN dnf clean all 
  6. RUN rm -rf /var/cache/yum 
  7.  
  8. [chris@krang] $ docker build -t squash -f Dockerfile-squash --squash . 
  9. [chris@krang] $ docker images --format "{{.Repository}}: {{.Size}}"  | head -n 1 
  10. squash: 271 MB 

利用docker squash處理這個(gè)多層Dockerfile,我們最終得到了一個(gè)大小為271 MB的鏡像,且功能與之前的鏈接指令鏡像一樣。但這,又帶來(lái)了新的潛在問題。

太過極端:過度壓縮、過度“瘦身”、過度專用

鏡像之間可以進(jìn)行層共享。其基礎(chǔ)可能為x MB,但只需要拉取/存儲(chǔ)一次,其他鏡像就能夠加以使用。進(jìn)行層共享的各鏡像的實(shí)際大小,為基礎(chǔ)層加上特定變化帶來(lái)的差異。通過這種方式,我們能夠以極低的額外空間投入,換取數(shù)千套基于同一鏡像的修改版鏡像。

而這也正是鏡像壓縮或者專用化方法帶來(lái)的弊端。在將鏡像壓縮為單層形式時(shí),我們將徹底失去其與其他鏡像進(jìn)行層共享的機(jī)會(huì)。每套鏡像最終都將與其單一層的體積保持一致。因此,如果大家只需要使用少量鏡像并在其中運(yùn)行大量容器,那么過度壓縮還沒什么問題; 但如果您面對(duì)著多種不同鏡像,那么從長(zhǎng)遠(yuǎn)角度來(lái)看,這最終反而會(huì)消耗您的存儲(chǔ)空間。

讓我們重新審視Nginx壓縮示例,可以看到在這種情況下,“瘦身”過程并不會(huì)帶來(lái)什么問題。我們最終安裝了Fedora與Nginx,清理了緩存,并進(jìn)行了有效壓縮。不過,Nginx本身并沒有多大作用,大家通常需要以自定義方式執(zhí)行各類針對(duì)性操作——例如配置文件、其他軟件包甚至是某些應(yīng)用代碼。而其中每一項(xiàng)操作都會(huì)在Dockerfile中添加更多指令。

如果以傳統(tǒng)方式進(jìn)行鏡像構(gòu)建,那么您將在鏡像中擁有一個(gè)承載Fedora的獨(dú)立基礎(chǔ)鏡像層,一個(gè)安裝有Nginx的層(包含或不包含緩存),而后每項(xiàng)自定義又有自己的層。包含F(xiàn)edora與Nginx等的其他鏡像將能夠共享這些層。

在這種情況下,需要的鏡像為:

 

  1. [   App 1 Layer (  5 MB) ]          [   App 2 Layer (6 MB) ] 
  2. [   Nginx Layer ( 21 MB) ] ------------------^ 
  3. [ Fedora  Layer (249 MB) ]   

但如果大家對(duì)該鏡像進(jìn)行壓縮,那么Fedora基礎(chǔ)層也會(huì)被壓縮。基于Fedora的被壓縮鏡像需要釋放相關(guān)Fedora內(nèi)容,這意味著每套鏡像將新增249 MB!

  1. [ Fedora + Nginx + App 1 (275 MB)] [ Fedora + Nginx + App 2 (276 MB) ] 

如果大家構(gòu)建出大量高度專用且超級(jí)小巧的鏡像,那么這絕對(duì)會(huì)帶來(lái)大麻煩。

因?yàn)榕c生活中的其他事務(wù)一樣,適度才是鏡像體積控制的關(guān)鍵所在。而且考慮到鏡像層的工作原理,隨著容器鏡像的壓縮度與專用性逐漸提高,其將無(wú)法與其他相關(guān)鏡像共享基礎(chǔ)鏡像層,而壓縮帶來(lái)的瘦身效果也將因此遞減甚至消失。

經(jīng)過一定程度自定義的鏡像可以共享基礎(chǔ)層。如前文所述,這一基礎(chǔ)層可以是x MB,但只需要進(jìn)行一次拉取/存儲(chǔ),所有鏡像就都能夠?qū)ζ浼右允褂谩K戌R像的有效大小為基礎(chǔ)層加上每種特定變化造成的差異。通過這種方式,我們能夠以極低的額外空間投入,換取數(shù)千套基于同一鏡像的修改版鏡像。

 

  1. [ specific app   ]      [ specific app 2 ] 
  2. [ customizations ]--------------^ 
  3. [ base layer     ] 

但如果您的鏡像壓縮得太狠或者存在太多修改或?qū)S谜{(diào)整,那么我們將不得不面對(duì)大量鏡像。由于這些鏡像之間沒有同一套共享基礎(chǔ)層,因此其將各自占用磁盤上的存儲(chǔ)空間。

  1. [ specific app 1 ] [ specific app 2 ] [ specific app 3 ] 

總結(jié)

我們擁有多種能夠有效減少容器鏡像所需存儲(chǔ)空間與傳輸帶寬的處理方法,但其中最有效的方法無(wú)疑是降低鏡像本身的體積。無(wú)論您選擇單純清理其中的緩存(避免將其保留在中間層內(nèi))、將全部層壓縮為單一層,或者只是在空鏡像中添加靜態(tài)二進(jìn)制文件,大家都有必要花些時(shí)間研究鏡像中可能存在的不必要內(nèi)容,并將其縮小至合理的水平。

原文標(biāo)題:Building tiny container images,作者:Chris Collins

【51CTO譯稿,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文譯者和出處為51CTO.com】

責(zé)任編輯:未麗燕 來(lái)源: 51CTO.com
相關(guān)推薦

2024-01-15 00:11:04

Docker網(wǎng)絡(luò)系統(tǒng)

2023-09-21 10:15:40

數(shù)據(jù)中心服務(wù)器

2019-03-28 10:20:09

容器鏡像系統(tǒng)運(yùn)維Linux

2011-11-25 10:25:27

SpringJava

2025-03-26 08:00:00

2009-06-19 18:26:38

Spring事務(wù)配置

2011-02-28 13:51:30

Spring事物配置

2010-08-27 09:10:15

網(wǎng)絡(luò)隱私

2021-12-07 07:58:33

工具效率Typora

2016-12-23 14:58:58

容器WebSocketDocker Remo

2018-09-10 15:58:49

2022-12-27 14:21:42

VR

2010-08-13 13:25:53

Flex頁(yè)面跳轉(zhuǎn)

2017-07-04 16:34:33

邊緣計(jì)算方式

2022-01-13 19:25:28

服務(wù)方式TCP

2023-07-25 10:45:48

OHScrcpy鴻蒙

2019-12-16 12:11:53

Docker容器Kubernetes

2023-01-13 07:41:20

BeanSpring容器

2009-10-29 16:41:23

2021-11-25 10:02:59

物聯(lián)網(wǎng)IOT
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 一区二区电影 | 天堂在线一区 | 亚洲少妇综合网 | 欧美日韩视频在线播放 | 97国产成人 | 日韩男人天堂 | 国产乱码精品一区二区三区中文 | 精品欧美| 日本欧美视频 | 精品三级在线观看 | 中文字幕电影在线观看 | 四虎影院欧美 | 懂色av色香蕉一区二区蜜桃 | 欧美一级精品片在线看 | 亚洲一区 中文字幕 | 日韩在线欧美 | 在线免费观看黄色 | 国产激情网站 | 国产精品入口麻豆www | 亚洲精品99久久久久久 | 亚州精品天堂中文字幕 | 久久激情av | 成人三级av | 91国自产| 国产在线网站 | 国产精品欧美大片 | 日韩欧美成人一区二区三区 | 亚洲精品久久久一区二区三区 | 毛片免费看的 | 日韩亚洲视频在线 | 国产91亚洲精品 | 亚洲成网 | 91玖玖 | 免费观看a级毛片在线播放 黄网站免费入口 | 午夜成人免费视频 | 久草久草久草 | 一级黄色片免费在线观看 | 国产欧美日韩综合精品一区二区 | 欧美一级网站 | 久久久网 | 欧美在线一区二区三区 |