多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > 互聯網 > Docker:分布式系統的軟件工程革命(上)

Docker:分布式系統的軟件工程革命(上)

來源:程序員人生   發布時間:2014-09-06 17:37:07 閱讀次數:2419次

【編者按】為了使用Docker,需要了解不少工具及其設計思路;而這些工具的文檔分布在不同的網站。為了方便大家學習,本文以開發一個極簡的搜索引擎為例,展示Docker帶來的革新。本文以技術教程為主線,穿插了一些關于Hadoop和Mesos等“模仿”項目的介紹,簡要追溯它們勇敢而艱難的“邯鄲學步”的歷程。本文來自王益的個人博客。


免費訂閱“CSDN大數據”微信公眾號,實時了解最新的大數據進展!

CSDN大數據,專注大數據資訊、技術和經驗的分享和討論,提供Hadoop、Spark、Imapala、Storm、HBase、MongoDB、Solr、機器學習、智能算法等相關大數據觀點,大數據技術,大數據平臺,大數據實踐,大數據產業資訊等服務。


以下為原文:

Docker最近很火。Docker實現了“集裝箱”――一種介于“軟件包”和“虛擬機”之間的概念――并被寄予厚望,以期革新Internet服務以及其他大數據處理系統的開發、測試、和部署流程。

為了使用Docker,需要了解不少工具及其設計思路;而這些工具的文檔分布在不同的網站。為了方便大家學習,本文以開發一個極簡的搜索引擎為例,展示Docker帶來的革新。

說是革新,其實是Google已經用了很多年的方式,只是最近才因為Docker開源項目而廣為人知。最近這將近十年的時間里,各互聯網公司和高校都在奮力模仿Google的計算技術。了解這一模仿的過程,可以幫助我們深入理解分布式系統(包括現在常說的“大數據系統”)中若干重要問題。為此,本文以技術教程為主線,穿插了一些關于Hadoop和Mesos等“模仿”項目的介紹,簡要追溯它們勇敢而艱難的“邯鄲學步”的歷程。最后,本文會介紹Google最近公布的“正確答案”――Hubernetes――Google核心技術Borg的開源版本。

Docker

Docker是一個軟件系統,實現了一種稱為“集裝箱”的概念。集裝箱類似Google機群管理系統Borg中的包(package)。

通常我們說的“包”是軟件包――比如Ubuntu/Debian Linux里常見的.deb文件――安裝的時候,安裝程序會把被依賴的包也裝上。可是執行的時候呢?得根據具體情況配置,然后依次啟動互相依賴的多個程序。比如,啟動一個Web服務之前,要啟動Apache和MySQL;而且他們仨都得有合理的配置,確保它們能一起工作,來實現這個Web服務。

但是Docker集裝箱以及Borg中的包更像虛擬機。虛擬機里包括程序和配置,所以可以被執行――也就是執行其中的程序。因為程序是配置好的,所以虛擬機可以被扔到各種環境上去執行――包括開發機、做演示用的筆記本電腦、用VirtualBox虛擬的機群、測試機群、預發布環境和產品環境。近幾年隨著“云計算”概念的普及,虛擬機被廣泛使用,作為分布式計算的基礎調度單元。

Docker作為一個軟件系統,可以用來創建“集裝箱鏡像”(container image)和執行這些鏡像。就像VirtualBox是一個軟件系統,可以用來創建和執行虛擬機。但是集裝箱比虛擬機“輕”――一個虛擬機包括一組虛擬硬件、操作系統,用來執行用戶程序;而集裝箱里沒有虛擬的硬件,也沒有操作系統,它用主機(host)的硬件和操作系統來執行程序。

那么在集裝箱里跑程序和直接在主機上跑有什么區別呢?一個區別是,集裝箱有一套網絡端口空間(port space)。一個集裝箱里的進程可以各自開端口,也可以連接對方的端口進行通信。但是這些端口是集裝箱之外的進程看不到的。我們也可以讓集裝箱把某些內部端口號展示給外部,比如把集裝箱內的端口5000映射到外部的8080。這樣,當我們用主機上的程序(比如瀏覽器)訪問本機(主機)的8080端口時,實際上訪問的是集裝箱里的5000端口。這項對外公開集裝箱內部端口的技術,稱為端口轉發(port forwarding),和虛擬機的端口轉發概念一樣。另一個區別在于,集裝箱里有虛擬的文件系統。這樣我們可以把要執行的程序拷貝進集裝箱。也可以把主機上的某些目錄映射成集裝箱虛擬文件系統的某些目錄。

集裝箱這個想法已經在深刻地改變傳統分布式系統的開發、測試和部署的流程了。傳統的做法是,開發者寫一個Makefile(或者其他描述,比如CMakeList、POM等)來說明如何把源碼編譯成二進制文件。隨后,開發人員會在開發機上配置并且執行二進制文件,來作測試。測試人員會在測試機群上配置和執行,來作驗證。而運維人員會在數據中心里的預發布環境和產品環境上配置和執行,這就是部署。因為開發機、測試機群、和產品環境里機器的數量和質量都不同,所以配置往往很不同。加上每個新版本的軟件系統,配置方式難免有所差異,所以經常造成意外錯誤。以至于絕大部分團隊都選擇趁夜深人靜、用戶不活躍的時候,上線新版本,苦不堪言。

而利用集裝箱概念的開發流程里,開發者除了寫Makefile,還要寫一個Dockerfile,來描述如何把二進制文件安裝進一個集裝箱鏡像(container image),并且做好配置。而一個鏡像就像一臺配置好的虛擬機,可以在機群上啟動多個實例(instance),而每個實例通常稱為一個集裝箱(container)。在自測的時候,開發者在開發機上執行一個或者多個集裝箱;在驗證時,測試人員在測試機群上執行集裝箱;在部署時,運維人員在產品環境執行集裝箱。因為執行的都是同樣地集裝箱,所以不容易出錯。

這種流程更合理的劃分了開發者和其他角色的工作邊界,也大大簡化了測試和部署工作。

boot2docker

上節提到,Docker虛擬了網絡地址空間和文件系統。實際上,它還虛擬了進程ID空間(pid space)等系統數據結構。這些功能是一個叫dockerd的daemon程序借助Linux內核中的control groups(又叫cgroups)功能實現的。

dockerd負責執行集裝箱;就像VirtualBox負責執行虛擬機一樣。而cgroup是Google的兩個工程師Paul Menage和Rohit Seth貢獻給Linux社區的。從他們的工作記錄看,主要工作集中在2008和2009年。據說,Google開發它就是為了方便在自己的機群上部署各種Internet應用和離線處理系統。具體一點兒的故事,請看這篇Information Week上的帖子。。

因為cgroups功能只有Linux內核有,所以Docker目前只能運行在Linux上。可是,現在很多開發者都在用Mac。為了能讓這些開發者方便的測試自己創作的集裝箱鏡像,Docker的開發者寫了boot2docker――利用VirtualBox虛擬一個Linux主機,并且在上面安裝dockerd。而命令行控制程序docker執行在Mac主機上,被配置成和虛擬Linux主機上的dockerd協作。

boot2docker的安裝方式很簡單:照著這個流程,下載并執行一個安裝包即可。因為boot2docker利用了VirtualBox,所以安裝它之前需要先裝VirtualBox。Homebrew也提供了安裝boot2docker的選項,但是可能因為bug導致dockerd和docker版本不同,沒法協同工作。

在利用boot2docker在Mac上開始工作之前,還有幾個注意事項。當我們在Linux主機上啟動一個集裝箱的時候,我們可以讓Docker把主機的某些目錄映射成集裝箱內的目錄。這樣集裝箱里的程序和主機上的程序共享數據,是一種方便的調試方式。但是在用boot2docker的時候,“主機”不是Mac,而是虛擬Linux主機。此時如果想把Mac上的目錄映射到集裝箱,先得將其通過VirtualBox映射到Linux主機。

另一個注意事項和端口轉發有關。當我們把集裝箱內的某個端口映射為主機的某個端口時,只是映射到了虛擬Linux主機;如果想讓Mac上的程序能訪問,還得把虛擬機端口通過VirtualBox映射成Mac上的端口。這些注意事項,在下文中會有詳細解釋。

CoreOS

實際開發中的測試機群和產品環境通常都是用的Linux服務器。要在上面執行集裝箱,也需要安裝Docker。因為Docker的開發者提供各種Linux軟件包,所以通常輸入一個命令,即可安裝Docker。比如在Ubuntu/Debian Linux里,這個命令是:

sudo apt-get install docker.io

但是目前最常用的用來執行Docker集裝箱的Linux發行版本既不是Ubuntu、Debian也不是RedHat、Fedora,而是CoreOS。這個發行版本根本沒有軟件包管理程序,所以也不能通過輸入某個命令來安裝軟件。但是CoreOS預裝了Docker,所以可以制作集裝箱鏡像,或者下載別人發布的集裝箱鏡像來執行。目前,Amazon AWS和Google Compute Engine這兩大云計算平臺都提供預裝了CoreOS的虛擬機。

實際上,Google數據中心里運行的Linux系統和CoreOS有很多相似之處。我記得2010年我剛離開Google加入騰訊的時候,一位騰訊的同事好奇地問:“Google的機群里用的Linux用什么軟件包管理程序?是apt-get嗎?還是yum?”我回答:“其實服務器上運行的Linux是不需要包管理的,只有桌面Linux系統才需要”。這位同事很難相信。其實,要不是因為“見了一回豬跑”,我也想不到會是這樣。

CoreOS和其他Linux發行版本相比,執行效率高、內存耗費省;此外,利用雙磁盤分區技術,即便是更新Linux內核也不需要重啟。CoreOS還有很多獨特之處,使得它在問世后很短的時間里就被Amazon和Google采用。如果想進一步了解這些特性,請看這個對Docker作者的訪談。

Go語言

接下來,我們看看如何在Mac上用Go語言寫一個極簡化的搜索引擎,并且封裝成集裝箱鏡像。

我們選擇Go語言為例,而不是更常見的Java、Python、Perl、Ruby、Scala等,有很現實的原因――后面這些語言寫的程序,在執行時都需要某些運行環境的支持。比如,Java程序依賴Java虛擬機,Python程序需要Python解釋器,這些加上預裝的程序庫需要占用幾百MB的集裝箱空間。而用Go寫的程序默認是全靜態編譯的,執行時不需要任何環境支持,不需要預裝庫,甚至連Linux系統動態庫都不依賴。鑒于一家公司的系統往往由成千上萬的集裝箱構成;每個集裝箱少幾百MB,能為公司省出很大一筆開銷。那些每月要向Amazon或者Goolgle付賬的公司,對此必然印象深刻。這是Go語言在很多創業公司拓展迅猛的一個原因。

如果我們用C或者C++開發,也可以生成全靜態鏈接的二進制程序文件。但是在Web時代,C/C++的開發效率不如Go。Google里倒是普遍使用C++,但是Google里有一套精心設計、積攢多年的C++庫,這是外界沒有的。外界普遍得使用第三方庫,并往往因此撓頭。比如,不同的第三方庫(Thrift和boost)各有各的線程池機制,很難統一管理多線程。C++11倒是有了標準線程管理,但是把很多庫統一到C++11是一項開銷極大的工作。Go語言是專門為分布式系統開發設計的,根本就沒有線程的概念,在語法上用goroutine代替了,線程池實現在Go runtime里,被編譯進每個二進制程序。

交叉編譯

因為集裝箱用主機的操作系統和硬件來執行程序,而Docker只支持Linux,所以Go程序必須被編譯成Linux二進制文件,才能通過Docker運行。而我們在Mac上開發,需要利用交叉編譯技術來生成Linux二進制文件。

為了得到一個支持交叉編譯的Go語言編譯器,我們需要從源碼安裝Go,并且需要做一些額外的安裝工作。具體過程如下:

1. 安裝Xcode,從而獲得C編譯器。

2. 下載Go編譯器的源碼包。比如Go 1.3在這里。

3. 解壓和編譯

 tar xzvf go1.3.src.tar.gz
 cd go/src
 ./all.bash

4. 編譯各種平臺下的Go標準庫

 git clone git://github.com/davecheney/golang-crosscompile.git
 source golang-crosscompile/crosscompile.bash
 go-crosscompile-build-all

這里,我們用到了Dave Cheney寫的一個Bash腳本程序。這個程序支持生成以下平臺上的Go語言標準庫:

  1. darwin/386
  2. darwin/amd64
  3. freebsd/386
  4. freebsd/amd64
  5. freebsd/arm
  6. linux/386
  7. linux/amd64
  8. linux/arm
  9. windows/386
  10. windows/amd64
  11. nacl/amd64
  12. nacl/386

并行計算最常用的目標平臺是linux/amd64――64bit的Linux系統,也是CoreOS的平臺格式。下文中我們會演示如何在Mac下用這個編譯器生成Linux平臺的二進制代碼文件。

極簡版搜索引擎

在這篇帖子里,作者Adriaan de Jonge用一個最簡單的http server作為例子,說明如何在Mac下用Docker運行一個程序。

這篇帖子對我很有幫助。只是這個例子程序太過簡單了――通常一個互聯網產品包含不只一個程序――現代互聯網產品幾乎都采用micro service架構,一個http server和多個RPC server協同工作。之外,還會有一些daemon程序,不時向RPC server提供不斷更新的數據。比如在搜索引擎里,一個indexer程序會不斷將cralwer程序爬下來的網頁內容加以整理,并且發送給搜索引擎服務。

本節里我們介紹的極簡版的搜索引擎就包括兩個程序――search engine server和向它提供索引內容的indexer daemon。search engine server首先是一個http server,可以通過瀏覽器訪問――對每個輸入的query,返回相應的結果。同時,它還是一個RPC server,接受從indexer daemon發來的更新后的索引內容。這兩個程序的源碼在這里。

為了下載和構建這個例子程序,請輸入如下命令:

mkdir -p /tmp/learn-docker
cd /tmp/learn-docker
export GOPATH=`pwd`
go get github.com/wangkuiyi/helloworld/indexer
go get github.com/wangkuiyi/helloworld/searchengine

此時,在 /tmp/learn-docker/bin 目錄里應該有兩個二進制程序文件 indexer和searchengine。這兩個文件都是Darwin/AMD64格式的。我們可以在Mac主機上運行它倆:

./bin/searchengine -addr=":10000" &
./bin/indexer -searchengine="localhost:10000"

這樣首先啟動了searchengine,并且讓它的http和rpc服務都監聽本機(Mac主機)的10000端口;隨后啟動了indexer,它每秒鐘通過RPC調用告訴searchengine更新索引內容。

啟動成功之后,我們可以在瀏覽器里訪問如下網址:http://localhost:10000/?q=news,從而看到searchengine返回的搜索結果(如下圖):


當然,我們也可以用命令行程序,比如wget和curl,來訪問searchengine服務。這樣我們可以很方便的寫一個集成測試(regression test)程序。比如這個。

創建集裝箱

接下來,我們看看如何把這兩個程序打包進Docker集裝箱鏡像,然后在Mac主機(實際上是boot2docker創建的Linux虛擬機)上運行集裝箱。接下來我們會看到:這些集裝箱不用修改,也就能在Amazon AWS和Google Compute Engine上運行,從而完成發布。

首先,我們需要從源碼生成Linux/AMD64二進制程序文件。用上文介紹的方法,得到一個支持交叉編譯的Go編譯器之后,編譯示范程序很簡單:

GOOS=linux GOARCH=amd64 go install
github.com/wangkuiyi/helloworld/indexer
github.com/wangkuiyi/helloworld/searchengine

可以看到,我們只是通過環境變量設置了一下目標操作系統和架構。

隨后,我們要創建一個Docker集裝箱鏡像,把編譯好的兩個程序放進去。因為如上文介紹的,Go程序執行時不需要特殊的運行環境,所以這個集裝箱鏡像里,除了一些metadata和我們的程序之外,什么都不需要。以至于我們可以從Docker Hub網站上下載一個空的鏡像,在里面安裝我們的程序即可。為此,我們需要寫一個Dockerfile:

FROM scratch
ADD bin/linux_amd64/searchengine /searchengine
ADD bin/linux_amd64/indexer /indexer

這里的第一行是讓Docker自動從Docker Hub上下載名為scratch的鏡像;第二行說把本地文件bin/linux_amd64/searchengine裝進這個鏡像的根目錄,成為/searchengine;第三行拷貝indexer。

有了Dockerfile我們就能用docker命令創建一個鏡像了。下面命令創建一個鏡像,并命名為wangkuiyi/helloworld:

cp $GOPATH/src/github.com/wangkuiyi/helloworld/Dockerfile $GOPATH/
docker build -t wangkuiyi/helloworld $GOPATH

此時,我們可以用docker images命令看到我們創建的鏡像:

yiwang@yiwang-mn1-> docker images

REPOSITORY             TAG                 IMAGE ID            CREATED             VIRTUAL SIZE

wangkuiyi/helloworld   latest              255460c3d095        3 hours ago         13.86 MB

分布式系統的部署

最簡單的使用Docker的部署方案是:啟動一個集裝箱,在其中運行一個searchengine進程和一個indexer進程。這和上文中介紹的在Mac主機上運行的方式是一樣的,但這不符合分布式系統的一般部署原則。

通常,為了提高處理速度、提升吞吐量和系統容錯能力,每個程序都會啟動為多個進程,運行在不同的機器上。比如,indexer程序的每個進程處理一部分數據(比如一個cralwer進程的輸出)。這樣的并行處理提升建立索引的效率。這種情況下,每個進程及其處理的數據被稱為一個shard。(shard應該怎么翻譯?我不知道)。

類似地,searchengine進程也會啟動為多個進程,每個進程的內存空間里都裝著同樣地索引結構,所以都能提供同樣地服務,從而提升吞吐量。如果這些進程運行在不同的機器上,那么哪怕某些機器掛了,還有活著的進程能不間斷地提供搜索服務。這樣的每個進程被稱為一個replication。

其實每個indexer shard也可以是一組多個進程,其中每個進程是隸屬本shard的一個replication。從而同時提升indexer的處理速度和容錯能力。

這么多進程應該啟動在哪些機器上呢?要靠人來決定,可就忙不過來咯;得靠機群管理系統。Google Borg就是這樣一套系統。

可是在很多年的時間里,外界都不知道Borg。有一些項目試圖模仿Google的計算架構,比如Hadoop意圖模仿MapReduce。Google MapReduce是一個構建在Borg之上的并行計算框架。但是Hadoop的開發者沒有開發類似Borg的系統,而是讓Hadoop(計算框架)兼任資源管理和調度的功能,導致系統復雜,代碼亂作一團。

實際上,在Hadoop開始的若干年里,甚至沒有像Google MapReduce那樣讓每個job有一個master進程來管理;而是讓機群上所有job里的所有進程都向一個叫Job Tracker的進程匯報心跳(heartbeat),以至于一個Hadoop機群不能太大,否則Job Tracker會處理不過來。而且Job Tracker作為性能和穩定性的雙重瓶頸,一旦累壞了,整個機群上所有job就都掛了。Hadoop的開發者直到2011年左右才意識到這一點,并發布了一篇文章,開始計劃開發“下一代Hadoop”,現在被稱為YARN的系統。

YARN的功能和Google Borg有類似之處,但是真正引發外界對Google Borg關注的,是加州大學伯克利分校和Twitter的合作項目Mesos。這是一個試圖復制Borg的嘗試。當Mesos在Twitter運行起來的時候,很多從Google加入Twitter的工程師都很興奮――終于重新能“高效工作”了!這里的故事,可以參見這篇Wired文章。Mesos系統設計思路描述在這篇論文里。其第一作者Ben Hindman曾經在Google實習,后來在Twitter任職。

實際上,即便Mesos也沒有能很相似地模仿Google Borg。至少在程序的發布和部署上。Mesos沒有和Google Borg等效的打包和執行包的功能。而這個功能能為外界所訪問,正是靠了本文著重介紹的Docker。Docker和Google Borg一樣,使用Google工程師為Linux內核貢獻的cgroups功能來實現集裝箱機制。

借助Docker,Google終于于本月(2014年7月)開源了Borg――但是是用Go語言重寫的Borg,稱為Kubernetes――Google Borg是用C++開發的。感謝開源社區不懈的推動!

集成測試

基于上一節的介紹,我們能想象,如果每個集裝箱只執行一個進程,那么機群管理系統在部署和調度應用時受到的限制最少。反過來想,如果我們在一個集裝箱里同時運行一個indexer進程和一個searchengine進程,那么我們實際上引入了一個不必要的約束――indexer進程和searchengine進程一一對應。而且如果機群中有一臺機器,可以承擔運行一個進程的負載,但是不能承擔同時運行兩個進程,那么這臺機器上就沒法部署上述“大”集裝箱了。

所以,在Google Borg和Google Kubernetes里,都建議每個集裝箱里只執行一個進程。

基于“打包一次,兼顧測試和發布”的原則,我們可以想象,對于一個應用(或者叫做產品,比如上述的極簡搜索引擎),最常見的打包方式是產生一個集裝箱鏡像,但是每個集裝箱里只執行一個程序的一個進程。

上文中,我們已經用一個Dockerfile把兩個程序:indexer和searchengine都裝進一個鏡像wangkuiyi/hellworld了。接下來,我們嘗試在Mac主機上啟動兩個集裝箱,分別執行一個indexer和一個searchengine進程:

docker run -d -p 8080:8080 --name searchengine wangkuiyi/helloworld /searchengine
VBoxManage modifyvm "boot2docker-vm" --natpf1 "tcp-port8080,tcp,,8080,,8080"
docker run -d --name indexer --link searchengine:se wangkuiyi/helloworld /indexer -searchengine=se:8080

這里,第一行啟動了一個集裝箱,并且起名叫searchengine,執行的鏡像是wangkuiyi/helloworld。-d的意思是在后臺執行,類似一個shell命令后面跟上一個&符號的效果。-p 8080:8080的意思是:“這個集裝箱里有個程序會監聽8080端口(如果看看searchengine的源碼,會發現8080是其默認端口),把這個端口映射到主機(boot2docker創建的Linux虛擬機)的8080端口”。

第二個命令讓VirtualBox把Linux虛擬機的8080端口映射為Mac主機的8080端口。這樣就可以在Mac主機上啟動一個瀏覽器,通過訪問本機的8080端口,來訪問集裝箱里的searchengine服務。(如果你在Linux主機上開發,就不需要boot2docker虛擬一個Linux主機了,也就不需要這個命令了。)

上述第三個命令啟動了一個名為indexer的集裝箱,執行的也是wangkuiyi/helloworld鏡像。在這個集裝箱里啟動了一個indexer進程;這個進程會去連接se:8080這個網絡地址,并通過RPC調用,向這個目標地址發送更新的索引數據。se這個IP地址是怎么來的呢?這是--link seachengine:se參數的效果――這個參數使得Docker在啟動indexer集裝箱之前,修改了其中/etc/hosts文件,在其中增加了一行:

 xxx.xxx.xxx.xxx se

這里 xxx.xxx.xxx.xxx 指代集裝箱searchengine(--link searchengine:se中冒號左邊的部分)的虛擬IP地址,se(--link searchengine:se中冒號右邊的部分)也就是其域名了。Docker就是通過--link這個參數,讓不同集裝箱內的多個進程可以互相通信的。

此時,在本機打開一個瀏覽器窗口并訪問http://localhost:8080/?q=news,可以看到和上圖完全一樣的結果。

自動部署

到目前為止,我們都是手動調用docker命令來操作docker的。而得到的效果――在Mac主機上啟動極簡搜索引擎――和不用Docker是一樣的。大家不禁會問,為什么要引入Docker呢?

其實,實際使用Docker時,我們不會手動敲docker命令,而是會利用fleet或者Kubernetes來部署和啟動集裝箱。這樣只需要寫一個非常簡明的部署配置文件,就可以在開發機、集成測試機群、預發布機群、和產品環境中完成部署了。這篇文章為了說明Docker的設計思路和使用方法已經很長了,所以關于fleet和Kubernetes的介紹,我準備放在《Docker:分布式系統的軟件工程革命(下)》中。

原文鏈接:Docker:分布式系統的軟件工程革命(上)(責編/魏偉)

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 日本久久综合视频 | 国产精品一页 | 久久91亚洲精品久久91综合 | 激情97| 亚洲一区二区三区高清 不卡 | freexx性欧美黑人 | 久久精品成人一区二区三区 | 国产一区二区三区欧美精品 | 亚洲一区精品伊人久久伊人 | 欧美激情一区二区三区在线播放 | 久久亚洲精品成人 | 黄色大片日本 | 亚洲精品久久久久久下一站 | 欧美性性性性性色大片免费的 | 成人私拍福利视频在线 | 最近更新中文字幕4 | 涩涩片影院 | 欧美人与物videos另 | 日韩欧美国产中文字幕 | 亚洲国产一级毛片 | 日韩久久精品一区二区三区 | 国产aⅴ精品一区二区三区久久 | 尤物国产视频 | 亚洲啪在线 | 久久久久亚洲精品影视 | 欧美一级毛片日本 | 一级片久久 | 欧美日韩不卡中文字幕在线 | 亚洲国产一成人久久精品 | 一道本一区二区三区 | 欧美刺激性色黄大片18 | 九九欧美 | 午夜爱爱免费视频 | 九九精品免费观看在线 | 亚欧美图片自偷自拍另类 | 五月婷婷激情四射 | 日韩亚洲欧美综合 | 亚洲人成在线影院 | 精品国产一区二区三区在线观看 | 一牛精品视频在线观看免费 | 亚洲综合网在线观看 |