日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Docker源码分析(十):Docker镜像下载

發布時間:2025/4/5 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Docker源码分析(十):Docker镜像下载 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://www.infoq.com/cn/articles/docker-source-code-analysis-part10

1.前言

說Docker Image是Docker體系的價值所在,沒有絲毫得夸大其詞。Docker Image作為容器運行環境的基石,徹底解放了Docker容器創建的生命力,也激發了用戶對于容器運用的無限想象力。

玩轉Docker,必然離不開Docker Image的支持。然而“萬物皆有源”,Docker Image來自何方,Docker Image又是通過何種途徑傳輸到用戶機器,以致用戶可以通過Docker Image創建容器?回憶初次接觸Docker的場景,大家肯定對兩條命令不陌生:docker pull和docker run。這兩條命令中,正是前者實現了Docker Image的下載。Docker Daemon在執行這條命令時,會將Docker Image從Docker Registry下載至本地,并保存在本地Docker Daemon管理的graph中。

談及Docker Registry,Docker愛好者首先聯想到的自然是Docker Hub。Docker Hub作為Docker官方支持的Docker Registry,擁有全球成千上萬的Docker Image。全球的Docker愛好者除了可以下載Docker Hub開放的鏡像資源之外,還可以向Docker Hub貢獻鏡像資源。在Docker Hub上,用戶不僅可以享受公有鏡像帶來的便利,而且可以創建私有鏡像庫。Docker Hub是全國最大的Public Registry,另外Docker還支持用戶自定義創建Private Registry。Private Registry主要的功能是為私有網絡提供Docker鏡像的專屬服務,一般而言,鏡像種類適應用戶需求,私密性較高,且不會占用公有網絡帶寬。

2.本文分析內容安排

本文作為《Docker源碼分析》系列的第十篇——Docker鏡像下載篇,主要從源碼的角度分析Docker下載Docker Image的過程。分析流程中,docker的版本均為1.2.0。

分析內容的安排如以下4部分:

(1) 概述Docker鏡像下載的流程,涉及Docker Client、Docker Server與Docker Daemon;

(2) Docker Client處理并發送docker pull請求;

(3) Docker Server接收docker pull請求,并創建鏡像下載任務并觸發執行;

(4) Docker Daemon執行鏡像下載任務,并存儲鏡像至graph。

3.Docker鏡像下載流程

Docker Image作為Docker生態中的精髓,下載過程中需要Docker架構中多個組件的協作。Docker鏡像的下載流程如圖3.1:

圖3.1 Docker鏡像下載流程圖

如上圖,下載流程,可以歸納為以上3個步驟:

(1) 用戶通過Docker Client發送pull請求,作用為:讓Docker Daemon下載指定名稱的鏡像;

(2) Docker Daemon中負責Docker API請求的Docker Server,接收Docker鏡像的pull請求,創建下載鏡像任務并觸發執行;

(3) Docker Daemon執行鏡像下載任務,從Docker Registry中下載指定鏡像,并將其存儲與本地的graph中。

下文即從三個方面分析docker pull請求執行的流程。

4.Docker Client

Docker架構中,Docker用戶的角色絕大多數由Docker Client來扮演。因此,用戶對Docker的管理請求全部由Docker Client來發送,Docker鏡像下載請求自然也不例外。

為了更清晰的描述Docker鏡像下載,本文結合具體的命令進行分析,如下:

docker pull ubuntu:14.04

以上的命令代表:用戶通過docker二進制可執行文件,執行pull命令,鏡像參數為ubuntu:14.04,鏡像名稱為ubuntu,鏡像標簽為14.04。此命令一經觸發,第一個接受并處理的Docker組件為Docker Client,執行內容包括以下三個步驟:

(1) 解析命令中與Docker鏡像相關的參數;

(2) 配置Docker下載鏡像時所需的認證信息;

(3) 發送RESTful請求至Docker Daemon。

4.1 解析鏡像參數

通過docker二進制文件執行docker pull ubuntu:14.04 時,Docker Client首先會被創建,隨后通過參數處理分析出請求類型pull,最終執行pull請求相應的處理函數。關于Docker Client的創建與命令執行可以參見《Docker源碼分析》系列第二篇——Docker Client篇。

Docker Client執行pull請求相應的處理函數,源碼位于./docker/api/client/command.go#L1183-L1244,有關提取鏡像參數的源碼如下:

func (cli *DockerCli) CmdPull(args ...string) error {cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry")tag := cmd.String([]string{"#t", "#-tag"}, "", "Download tagged image in a repository")if err := cmd.Parse(args); err != nil {return nil}if cmd.NArg() != 1 {cmd.Usage()return nil}var (v = url.Values{}remote = cmd.Arg(0))v.Set("fromImage", remote)if *tag == "" {v.Set("tag", *tag)}remote, _ = parsers.ParseRepositoryTag(remote)// Resolve the Repository name from fqn to hostname + namehostname, _, err := registry.ResolveRepositoryName(remote)if err != nil {return err}…… }

結合命令docker pull ubuntu:14.04,來分析CmdPull函數的定義,可以發現,該函數傳入的形參為args,實參只有一個字符串ubuntu:14.04。另外,縱觀以上源碼,可以發現Docker Client解析的鏡像參數無外乎4個:tag、remote、v和hostname,四者各自的作用如下:

  • tag:帶有Docker鏡像的標簽;
  • remote:帶有Docker鏡像的名稱與標簽;
  • v:類型為url.Values,實質是一個map類型,用于配置請求中URL的查詢參數;
  • hostname:Docker Registry的地址,代表用戶希望從指定的Docker Registry下載Docker鏡像。

4.1.1 解析tag參數

Docker鏡像的tag參數,是第一個被Docker Client解析的鏡像參數,代表用戶所需下載Docker鏡像的標簽信息,如:docker pull ubuntu:14.04請求中鏡像的tag信息為14.04,若用戶使用docker pull ubuntu請求下載鏡像,沒有顯性指定tag信息時,Docker Client會默認該鏡像的tag信息為latest。

Docker 1.2.0版本除了以上的tag信息傳入方式,依舊保留著代表鏡像標簽的flag參數tag,而這個flag參數在1.2.0版本的使用過程中已經被遺棄,并會在之后新版本的Docker中被移除,因此在使用docker 1.2.0版本下載Docker鏡像時,不建議使用flag參數tag。傳入tag信息的方式,建議使用docker pull NAME[:TAG]的形式。

Docker 1.2.0版本依舊保留的flag參數tag,其定義與解析的源碼位于:./docker/api/client/commands.go#1185-L1188,如下:

tag := cmd.String([]string{"#t", "#-tag"}, "", "Download tagged image in a repository") if err := cmd.Parse(args); err != nil {return nil }

以上的源碼說明:CmdPull函數解析tag參數時,Docker Client首先定義一個flag參數,flag參數的名稱為”#t”或者 “#-tag”,用途為:指定Docker鏡像的tag參數,默認值為空字符串;隨后通過cmd.Parse(args)的執行,解析args中的tag參數。

4.1.2 解析remote參數

Docker Client解析完tag參數之后,同樣需要解析出Docker鏡像所屬的repository,如請求docker pull ubuntu:14.04中,Docker鏡像為ubuntu:14.04,鏡像的repository信息為ubuntu,鏡像的tag信息為14.04。

Docker Client通過解析remote參數,使得remote參數攜帶repository信息和tag信息。Docker Client解析remote參數的第一個步驟,源碼如下:

remote = cmd.Arg(0)

其中,cmd的第一個參數賦值給remote,以docker pull ubuntu:14.04為例,cmd.Arg(0)為ubuntu:14.04,則賦值后remote值為ubuntu:14.04。此時remote參數即包含Docker鏡像的repository信息也包含tag信息。若用戶請求中帶有Docker Registry的信息,如docker pull localhost.localdomain:5000/docker/ubuntu:14.04,cmd.Arg(0)為localhost.localdomain:5000/docker/ubuntu:14.04,則賦值后remote值為localhost.localdomain:5000/docker/ubuntu:14.04,此時remote參數同時包含repository信息、tag信息以及Docker Registry信息。

隨后,在解析remote參數的第二個步驟中,Docker Client通過解析賦值完畢的remote參數,從中解析中repository信息,并再次覆寫remote參數的值,源碼如下:

remote, _ = parsers.ParseRepositoryTag(remote)

ParseRepositoryTag的作用是:解析出remote參數的repository信息和tag信息,該函數的實現位于./docker/pkg/parsers/parsers.go#L72-L81,源碼如下:

func ParseRepositoryTag(repos string) (string, string) {n := strings.LastIndex(repos, ":")if n < 0 {return repos, ""}if tag := repos[n+1:]; !strings.Contains(tag, "/") {return repos[:n], tag}return repos, "" }

以上函數的實現過程,充分考慮了多種不同Docker Registry的情況,如:請求docker pull ubuntu:14.04中remote參數為ubuntu:14.04,而請求docker pull localhost.localdomain:5000/docker/ubuntu:14.04中用戶指定了Docker Registry的地址localhost.localdomain:5000/docker,故remote參數還攜帶了Docker Registry信息。

ParseRepositoryTag函數首先從repos參數的尾部往前尋找”:”,若不存在,則說明用戶沒有顯性指定Docker鏡像的tag,返回整個repos作為Docker鏡像的repository;若”:”存在,則說明用戶顯性指定了Docker鏡像的tag,”:”前的內容作為repository信息,”:”后的內容作為tag信息,并返回兩者。

ParseRepositoryTag函數執行完,回到CmdPull函數,返回內容的repository信息將覆寫remote參數。對于請求docker pull localhost.localdomain:5000/docker/ubuntu:14.04,remote參數被覆寫后,值為localhost.localdomain:5000/docker/ubuntu,攜帶Docker Registry信息以及repository信息。

4.1.3 配置url.Values

Docker Client發送請求給Docker Server時,需要為請求配置URL的查詢參數。CmdPull函數的執行過程中創建url.Value并配置的源碼實現位于./docker/api/client/commands.go#L1194-L1203,如下:

var (v = url.Values{}remote = cmd.Arg(0))v.Set("fromImage", remote)if *tag == "" {v.Set("tag", *tag)}

其中,變量v的類型url.Values,配置的URL查詢參數有兩個,分別為”fromImage”與”tag”,”fromImage”的值是remote參數沒有被覆寫時值,”tag”的值一般為空,原因是一般不使用flag參數tag。

4.1.4 解析hostname參數

Docker Client解析鏡像參數時,還有一個重要的環節,那就是解析Docker Registry的地址信息。這意味著用戶希望從指定的Docker Registry中下載Docker鏡像。

解析Docker Registry地址的代碼實現位于./docker/api/client/commands.go#L1207,如下:

hostname, _, err := registry.ResolveRepositoryName(remote)

Docker Client通過包registry中的函數ResolveRepositoryName來解析hostname參數,傳入的實參為remote,即去tag化的remote參數。ResolveRepositoryName函數的實現位于./docker/registry/registry.go#L237-L259,如下:

func ResolveRepositoryName(reposName string) (string, string, error) {if strings.Contains(reposName, "://") {// It cannot contain a scheme!return "", "", ErrInvalidRepositoryName}nameParts := strings.SplitN(reposName, "/", 2)if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") && !strings.Contains(nameParts[0], ":") &&nameParts[0] != "localhost") {// This is a Docker Index repos (ex: samalba/hipache or ubuntu)err := validateRepositoryName(reposName)return IndexServerAddress(), reposName, err}hostname := nameParts[0]reposName = nameParts[1]if strings.Contains(hostname, "index.docker.io") {return "", "", fmt.Errorf("Invalid repository name, try \"%s\" instead", reposName)}if err := validateRepositoryName(reposName); err != nil {return "", "", err}return hostname, reposName, nil }

ResolveRepositoryName函數首先通過”/”分割字符串reposName,如下:

nameParts := strings.SplitN(reposName, "/", 2)

如果nameParts的長度為1,則說明reposName中不含有字符”/”,意味著用戶沒有指定Docker Registry。另外,形如”samalba/hipache”的reposName同樣說明用戶并沒有指定Docker Registry。當用戶沒有指定Docker Registry時,Docker Client默認返回IndexServerAddress(),該函數返回常量INDEXSERVER,值為”https://index.docker.io/v1”。也就是說,當用戶下載Docker鏡像時,若不指定Docker Registry,默認情況下,Docker Client通知Docker Daemon去Docker Hub上下載鏡像。例如:請求docker pull ubuntu:14.04,由于沒有指定Docker Registry,Docker Client默認使用全球最大的Docker Registry——Docker Hub。

當不滿足返回默認Docker Registry時,Docker Client通過解析reposNames,得出用戶指定的Docker Registry地址。例如:請求docker pull localhost.localdomain:5000/docker/ubuntu:14.04中,解析出的Docker Registry地址為localhost.localdomain:5000。

至此,與Docker鏡像相關的參數已經全部解析完畢,Docker Client將攜帶這部分重要信息,以及用戶的認證信息,構建RESTful請求,發送給Docker Server。

4.2 配置認證信息

用戶下載Docker鏡像時,Docker同樣支持用戶信息的認證。用戶認證信息由Docker Client配置;Docker Client發送請求至Docker Server時,用戶認證信息也被一并發送;隨后,Docker Daemon處理下載Docker鏡像請求時,用戶認證信息在Docker Registry被驗證。

Docker Client配置用戶認證信息包含兩個步驟,實現源碼如下:

cli.LoadConfigFile()// Resolve the Auth config relevant for this serverauthConfig := cli.configFile.ResolveAuthConfig(hostname)

可見,第一個步驟是使cli(Docker Client)加載ConfigFile,ConfigFile是Docker Client用來存放有關Docker Registry的用戶認證信息的對象。DockerCli、ConfigFile以及AuthConfig三種數據結構之間的關系如圖4.1:

圖4.1 DockerCli、ConfigFile以及AuthConfig關系圖

DockerCli結構體的屬性configFile為一個指向registry.ConfigFile的指針,而ConfigFile結構體的屬性Configs屬于map類型,其中key為string,代表Docker Registry的地址,value的類型為AuthConfig。AuthConfig類型具體含義為用戶在某個Docker Registry上的認證信息,包含用戶名,密碼,認證信息,郵箱地址等。

加載完用戶所有的認證信息之后,Docker Client第二個步驟是:通過用戶指定的Docker Registry,即之前解析出的hostname參數,從用戶所有的認證信息中找出與指定hostname相匹配的認證信息。新創建的authConfig,類型即為AuthConfig,將會作為用戶在指定Docker Registry上的認證信息,發送至Docker Server。

4.3 發送API請求

Docker Client解析完所有的Docker鏡像參數,并且配置完畢用戶的認證信息之后,Docker Client需要使用這些信息正式發送鏡像下載的請求至Docker Server。

Docker Client定義了pull函數,來實現發送鏡像下載請求至Docker Server,源碼位于./docker/api/client/commands.go#L1217-L1229,如下:

pull := func(authConfig registry.AuthConfig) error {buf, err := json.Marshal(authConfig)if err != nil {return err}registryAuthHeader := []string{base64.URLEncoding.EncodeToString(buf),}return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{"X-Registry-Auth": registryAuthHeader,})}

pull函數的實現較為簡單,首先通過authConfig對象,創建registryAuthHeader,最后發送POST請求,請求的URL為"/images/create?"+v.Encode(),在URL中傳入查詢參數包括”fromImage”與”tag”,另外在請求的HTTP Header中添加認證信息registryAuthHeader,。

執行以上的pull函數時,Docker鏡像下載請求被發送,隨后Docker Client等待Docker Server的接收、處理與響應。

5.Docker Server

Docker Server作為Docker Daemon的入口,所有Docker Client發送請求都由Docker Server接收。Docker Server通過解析請求的URL與請求方法,最終路由分發至相應的handler來處理。Docker Server的創建與請求處理,可以參看《Docker源碼分析》系列之Docker Server篇。

Docker Server接收到鏡像下載請求之后,通過路由分發最終由具體的handler——postImagesCreate來處理。postImagesCreate的實現位于./docker/api/server/server.go#L466-L524,的、其執行流程主要分為3個部分:

(1) 解析HTTP請求中包含的請求參數,包括URL中的查詢參數、HTTP header中的認證信息等;

(2) 創建鏡像下載job,并為該job配置環境變量;

(3) 觸發執行鏡像下載job。

5.1 解析請求參數

Docker Server接收到Docker Client發送的鏡像下載請求之后,首先解析請求參數,并未后續job的創建與運行提供參數依據。Docker Server解析的請求參數,主要有:HTTP請求URL中的查詢參數”fromImage”、”repo”以及”tag”,以及有HTTP請求的header中的”X-Registry-Auth”。

請求參數解析的源碼如下:

var (image = r.Form.Get("fromImage")repo = r.Form.Get("repo")tag = r.Form.Get("tag")job *engine.Job)authEncoded := r.Header.Get("X-Registry-Auth")

需要特別說明的是:通過”fromImage”解析出的image變量包含鏡像repository名稱與鏡像tag信息。例如用戶請求為docker pull ubuntu:14.04,那么通過”fromImage”解析出的image變量值為ubuntu:14.04,并非只有Docker鏡像的名稱。

另外,Docker Server通過HTTP header中解析出authEncoded,還原出類型為registry.AuthConfig的對象authConfig,源碼實現如下:

authConfig := ?istry.AuthConfig{}if authEncoded != "" {authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {// for a pull it is not an error if no auth was given// to increase compatibility with the existing api it is defaulting to be emptyauthConfig = ?istry.AuthConfig{}}}

解析出HTTP請求中的參數之后,Docker Server對于image參數,再次進行解析,從中解析出屬于repository與tag信息,其中repository有可能暫時包含Docker Registry信息,源碼實現如下:

if tag == "" {image, tag = parsers.ParseRepositoryTag(image)}

Docker Server的參數解析工作至此全部完成,在這之后Docker Server將創建鏡像下載任務并開始執行。

5.2 創建并配置job

Docker Server只負責接收Docker Client發送的請求,并將其路由分發至相應的handler來處理,最終的請求執行還是需要Docker Daemon來協作完成。Docker Server在handler中,通過創建job并觸發job執行的形式,把控制權交于Docker Daemon。

Docker Server創建鏡像下載job并配置環境變量的源碼實現如下:

job = eng.Job("pull", image, tag)job.SetenvBool("parallel", version.GreaterThan("1.3"))job.SetenvJson("metaHeaders", metaHeaders)job.SetenvJson("authConfig", authConfig)

其中,創建的job名為pull,含義是下載Docker鏡像,傳入參數為image與tag,配置的環境變量有parallel、metaHeaders與authConfig。

5.3 觸發執行job

Docker Server創建完Docker鏡像下載job之后,需要觸發執行該job,實現將控制權交于Docker Daemon。

Docker Server觸發執行job的源碼如下:

if err := job.Run(); err != nil {if !job.Stdout.Used() {return err}sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))w.Write(sf.FormatError(err))}

由于Docker Daemon在啟動時,已經配置了名為”pull”的job所對應的handler,實際為graph包中的CmdPull函數,故一旦該job被觸發執行,控制權將直接交于Docker Daemon的CmdPull函數。Docker Daemon啟動時Engine的handler注冊,可以參見《Docker源碼分析》系列的第三篇——Docker Daemon啟動篇。

6.Docker Daemon

Docker Daemon是完成job執行的主要載體。Docker Server為鏡像下載job準備好所有的參數配置之后,只等Docker Daemon來完成執行,并返回相應的信息,Docker Server再將響應信息返回至Docker Client。Docker Daemon對于鏡像下載job的執行,涉及的內容較多:首先解析job參數,獲取Docker鏡像的repository、tag、Docker Registry信息等;隨后與Docker Registry建立session;然后通過session下載Docker鏡像;接著將Docker鏡像下載至本地并存儲于graph;最后在TagStore標記該鏡像。

Docker Daemon對于鏡像下載job的執行主要依靠CmdPull函數。這個CmdPull函數與Docker Client的CmdPull函數完全不同,前者是為了代替用戶發送鏡像下載的請求至Docker Daemon,而Docker Daemon的CmdPull函數則是實現代替用戶真正完全鏡像下載的任務。調用CmdPull函數的對象類型為TagStore,其源碼實現位于./docker/graph/pull.go。

6.1 解析job參數

正如Docker Client與Docker Server,Docker Daemon執行鏡像下載job時的第一個步驟也是解析參數。解析工作一方面確保傳入參數無誤,另一方面按需為job提供參數依據。表6.1羅列Docker Daemon解析的job參數,如下:

表6.1 Docker Daemon解析job參數列表

參數名稱

參數描述

localName

代表鏡像的repository信息,有可能攜帶Docker Registry信息

tag

代表鏡像的標簽信息,默認為latest

authConfig

代表用戶在指定Docker Registry上的認證信息

metaHeaders

代表請求中的header信息

hostname

代表Docker Registry信息,從localName解析獲得,默認為Docker Hub地址

remoteName

代表Docker鏡像的repository名稱信息,不攜帶Docker Registry信息

endpoint

代表Docker Registry完整的URL,從hostname擴展獲得

?

參數解析過程中,Docker Daemon還添加了一些精妙的設計。如:在TagStore類型中設計了pullingPool對象,用于保存正在被下載的Docker鏡像,下載完畢之前禁止其他Docker Client發起相同鏡像的下載請求,下載完畢之后pullingPool中的該記錄被清除。Docker Daemon一旦解析出localName與tag兩個參數信息,則立即檢測pullingPool,實現源碼位于./docker/graph/pull.go#L36-L46,如下:

c, err := s.poolAdd("pull", localName+":"+tag)if err != nil {if c != nil {// Another pull of the same repository is already taking place; just wait for it to finishjob.Stdout.Write(sf.FormatStatus("", "Repository %s already being pulled by another client. Waiting.", localName))<-creturn engine.StatusOK}return job.Error(err)}defer s.poolRemove("pull", localName+":"+tag)

6.2 創建session對象

下載Docker鏡像,Docker Daemon與Docker Registry需要建立通信。為了保障兩者通信的可靠性,Docker Daemon采用了session機制。Docker Daemon每收到一個Docker Client的鏡像下載請求,都會創建一個與相應Docker Registry的session,之后所有的網絡數據傳輸都在該session上完成。包registry定義了session,位于./docker/registry/registry.go,如下:

type Session struct {authConfig *AuthConfigreqFactory *utils.HTTPRequestFactoryindexEndpoint stringjar *cookiejar.Jartimeout TimeoutType }

CmdPull函數中創建session的源碼實現如下:

r, err := registry.NewSession(authConfig, registry.HTTPRequestFactory (metaHeaders), endpoint, true)

創建的session對象為r,在下一階段的鏡像下載過程中,多數與鏡像相關的數據傳輸均在r這個seesion的基礎上完成。

6.3 執行鏡像下載

Docker Daemon之前所有的操作,都屬于配置階段,從解析job參數,到建立session對象,而并未與Docker Registry建立實際的連接,并且也還未真正傳輸過有關Docker鏡像的內容。

完成所有的配置之后,Docker Daemon進入Docker鏡像下載環節,實現Docker鏡像下載的源碼位于./docker/graph/pull.go#L69-L71,如下:

if err = s.pullRepository(r, job.Stdout, localName, remoteName, tag, sf, job.GetenvBool("parallel")); err != nil {return job.Error(err)}

以上代碼中pullRepository函數包含了鏡像下載整個流程的林林總總,該流程可以參見圖6.1:

圖6.1 pullRepository流程圖

關于上圖的各個環節,下表給出簡要的功能介紹:

表6.2 pullRepository各環節功能介紹表

函數名稱

功能介紹

r.GetRepositoryData()

獲取指定repository中所有image的id信息

r.GetRemoteTags()

獲取指定repository中所有的tag信息

r.pullImage()

從Docker Registry下載Docker鏡像

r.GetRemoteHistory()

獲取指定image所有祖先image id信息

r.GetRemoteImageJSON()

獲取指定image的json信息

r.GetRemoteImageLayer()

獲取指定image的layer信息

s.graph.Register()

將下載的鏡像在TagStore的graph中注冊

s.Set()

在TagStore中添加新下載的鏡像信息

?

分析pullRepository的整個流程之前,很有必要了解下pullRepository函數調用者的類型TagStore。TagStore是Docker鏡像方面涵蓋內容最多的數據結構:一方面TagStore管理Docker的Graph,另一方面TagStore還管理Docker的repository記錄。除此之外,TagStore還管理著上文提到的對象pullingPool以及pushingPool,保證Docker Daemon在同一時刻,只為一個Docker Client執行同一鏡像的下載或上傳。TagStore結構體的定義位于./docker/graph/tags.go#L20-L29,如下:

type TagStore struct {path stringgraph *GraphRepositories map[string]Repositorysync.Mutex// FIXME: move push/pull-related fields// to a helper typepullingPool map[string]chan struct{}pushingPool map[string]chan struct{} }

以下將重點分析pullRepository的整個流程。

6.3.1 GetRepositoryData

使用Docker下載鏡像時,用戶往往指定的是Docker鏡像的名稱,如:請求docker pull ubuntu:14.04中鏡像名稱為ubuntu。GetRepositoryData的作用則是獲取鏡像名稱所在repository中所有image的 id信息。

GetRepositoryData的源碼實現位于./docker/registry/session.go#L255-L324。獲取repository中image的ID信息的目標URL地址如以下源碼:

repositoryTarget := fmt.Sprintf("%srepositories/%s/images", indexEp, remote)

因此,docker pull ubuntu:14.04請求被執行時,repository的目標URL地址為https://index.docker.io/v1/repositories/ubuntu/images,訪問該URL可以獲得有關ubuntu這個repository中所有image的 id信息,部分image的id信息如下:

[{"checksum": "", "id": " 2427658c75a1e3d0af0e7272317a8abfaee4c15729b6840e3c2fca342fe47bf1"}, {"checksum": "", "id": "81fbd8fa918a14f4ebad9728df6785c537218279081c7a120d72399d3a5c94a5" }, {"checksum": "", "id": "ec69e8fd6b0236b67227869b6d6d119f033221dd0f01e0f569518edabef3b72c" }, {"checksum": "", "id": "9e8dc15b6d327eaac00e37de743865f45bee3e0ae763791a34b61e206dd5222e" }, {"checksum": "", "id": "78949b1e1cfdcd5db413c300023b178fc4b59c0e417221c0eb2ffbbd1a4725cc" },……]

獲取以上信息之后,Docker Daemon通過RepositoryData和ImgData類型對象來存儲ubuntu這個repository中所有image的信息,RepositoryData和ImgData的數據結構關系如圖6.2:

圖6.2 RepositoryData和ImgData的數據結構關系圖

GetRepositoryData執行過程中,會為指定repository中的每一個image創建一個ImgData對象,并最終將所有ImgData存放在RepositoryData的ImgList屬性中,ImgList的類型為map,key為image的ID,value指向ImgData對象。此時ImgData對象中只有屬性ID與Checksum有內容。

6.3.2 GetRemoteTags

使用Docker下載鏡像時,用戶除了指定Docker鏡像的名稱之外,一般還需要指定Docker鏡像的tag,如:請求docker pull ubuntu:14.04中鏡像名稱為ubuntu,鏡像tag為14.04,假設用戶不顯性指定tag,則默認tag為latest。GetRemoteTags的作用則是獲取鏡像名稱所在repository中所有tag的信息。

GetRemoteTags的源碼實現位于./docker/registry/session.go#L195-234。獲取repository中所有tag信息的目標URL地址如以下源碼:

endpoint := fmt.Sprintf("%srepositories/%s/tags", host, repository)

獲取指定repository中所有tag信息之后,Docker Daemon根據tag對應layer的ID,找到ImgData,并對填充ImgData中的Tag屬性。此時,RepositoryData的ImgList屬性中,有的ImgData對象有Tag內容,有的ImgData對象中沒有Tag內容。這也和實際情況相符,如下載一個ubuntu:14.04鏡像,該鏡像的rootfs中只有最上層的layer才有tag信息,這一層layer的parent Image并不一定存在tag信息。

6.3.3 pullImage

Docker Daemon下載Docker鏡像時是通過image id來完成。GetRepositoryData和GetRemoteTags則成功完成了用戶傳入的repository和tag信息與image id的轉換。如請求docker pull ubuntu:14.04中,repository為ubuntu,tag為14.04,則對應的image id為2d24f826。

Docker Daemon獲得下載鏡像的image id之后,首先查驗pullingPool,判斷是否有其他Docker Client同樣發起了該鏡像的下載請求,如果沒有的話Docker Daemon才繼續下載任務。

執行pullImage函數的源碼實現位于./docker/graph/pull.go#L159,如下:

s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf)

而pullImage函數的定義位于./docker/graph/pull.go#L214-L301。圖6.1中,可以看到pullImage函數的執行可以分為4個步驟:GetRemoteHistory、GetRemoteImageJson、GetRemoteImageLayer與s.graph.Register()。

GetRemoteHistory的作用很好理解,既然Docker Daemon已經通過GetRepositoryData和GetRemoteTags找出了指定tag的image id,那么Docker Daemon所需完成的工作為下載該image 及其所有的祖先image。GetRemoteHistory正是用于獲取指定image及其所有祖先iamge的id。

GetRemoteHistory的源碼實現位于./docker/registry/session.go#L72-L101。

獲取所有的image id之后,對于每一個image id,Docker Daemon都開始下載該image的全部內容。Docker Image的全部內容包括兩個方面:image json信息以及image layer信息。Docker所有image的json信息都由函數GetRemoteImageJSON來完成。分析GetRemoteImageJSON之前,有必要闡述清楚什么是Docker Image的json信息。

Docker Image的json信息是一個非常重要的概念。這部分json唯一的標志了一個image,不僅標志了image的id,同時也標志了image所在layer對應的config配置信息。理解以上內容,可以舉一個例子:docker build。命令docker build用以通過指定的Dockerfile來創建一個Docker鏡像;對于Dockerfile中所有的命令,Docker Daemon都會為其創建一個新的image,如:RUN apt-get update, ENV path=/bin, WORKDIR /home等。對于命令RUN apt-get update,Docker Daemon需要執行apt-get update操作,對應的rootfs上必定會有內容更新,導致新建的image所代表的layer中有新添加的內容。而如ENV path=/bin, WORKDIR /home這樣的命令,僅僅是配置了一些容器運行的參數,并沒有鏡像內容的更新,對于這種情況,Docker Daemon同樣創建一層新的layer,并且這層新的layer中內容為空,而命令內容會在這層image的json信息中做更新。總結而言,可以認為Docker的image包含兩部分內容:image的json信息、layer內容。當layer內容為空時,image的json信息被更新。

清楚了Docker image的json信息之后,理解GetRemoteImageJSON函數的作用就變得十分容易。GetRemoteImageJSON的執行代碼位于./docker/graph/pull.go#L243,如下:

imgJSON, imgSize, err = r.GetRemoteImageJSON(id, endpoint, token)

GetRemoteImageJSON返回的兩個對象imgJSON代表image的json信息,imgSize代表鏡像的大小。通過imgJSON對象,Docker Daemon立即創建一個image對象,創建image對象的源碼實現位于./docker/graph/pull.go#L251,如下:

img, err = image.NewImgJSON(imgJSON)

而NewImgJSON函數位于包image中,函數返回類型為一個Image對象,而Image類型的定義而下:

type Image struct {ID string `json:"id"`Parent string `json:"parent,omitempty"`Comment string `json:"comment,omitempty"`Created time.Time `json:"created"`Container string `json:"container,omitempty"`ContainerConfig runconfig.Config `json:"container_config,omitempty"`DockerVersion string `json:"docker_version,omitempty"`Author string `json:"author,omitempty"`Config *runconfig.Config `json:"config,omitempty"`Architecture string `json:"architecture,omitempty"`OS string `json:"os,omitempty"`Size int64graph Graph }

返回img對象,則說明關于該image的所有元數據已經保存完畢,由于還缺少image的layer中包含的內容,因此下一個步驟即為下載鏡像layer的內容,調用函數為GetRemoteImageLayer,函數執行位于./docker/graph/pull.go#L270,如下:

layer, err := r.GetRemoteImageLayer(img.ID, endpoint, token, int64(imgSize))

GetRemoteImageLayer函數返回當前image的layer內容。Image的layer內容指的是:該image在parent image之上做的文件系統內容更新,包括文件的增添、刪除、修改等。至此,image的json信息以及layer內容均被Docker Daemon獲取,意味著一個完整的image已經下載完畢。下載image完畢之后,并不意味著Docker Daemon關于Docker鏡像下載的job就此結束,Docker Daemon仍然需要對下載的image進行存儲管理,以便Docker Daemon在執行其他如創建容器等job時,能夠方便使用這些image。

Docker Daemon在graph中注冊image的源碼實現位于./docker/graph/pull.go#L283-L285,如下:

err = s.graph.Register(imgJSON,utils.ProgressReader(layer, imgSize, out, sf, false, utils.TruncateID(id), "Downloading"),img)

Docker Daemon通過graph存儲image是一個很重要的環節。Docker在1.2.0版本中可以通過AUFS、DevMapper以及BTRFS來進行image的存儲。在Linux 3.18-rc2版本中,OverlayFS已經被內核合并,故從1.4.0版本開始,Docker 的image支持OverlayFS的存儲方式。

Docker鏡像的存儲在Docker中是較為獨立且重要的內容,故將在《Docker源碼分析》系列的第十一篇專文分析。

6.3.4 配置TagStore

Docker鏡像下載完畢之后,Docker Daemon需要在TagStore中指定的repository中添加相應的tag。每當用戶查看本地鏡像時,都可以從TagStore的repository中查看所有含有tag信息的image。

Docker Daemon配置TagStore的源碼實現位于./docker/graph/pull.go#L206,如下:

if err := s.Set(localName, tag, id, true); err != nil {return err}

TagStore類型的Set函數定義位于./docker/graph/tags.go#L174-L205。Set函數的指定流程與簡要介紹如圖6.3:

圖6.3 TagStore中Set函數執行流程圖

當Docker Daemon將已下載的Docker鏡像信息同步到repository之后,Docker下載鏡像的job就全部完成,Docker Daemon返回響應至Docker Server,Docker Server返回相應至Docker Client。注:本地的repository文件位于Docker的根目錄,根目錄一般為/var/lib/docker,如果使用aufs的graphdriver,則repository文件名為repositories-aufs。

7.總結

Docker鏡像給Docker容器的運行帶來了無限的可能性,諸如Docker Hub之類的Docker Registry又使得Docker鏡像在全球的開發者之間共享。Docker鏡像的下載,作為使用Docker的第一個步驟,Docker愛好者若能熟練掌握其中的原理,必定能對Docker的很多概念有更為清晰的認識,對Docker容器的運行、管理等均是有百利而無一害。

Docker鏡像的下載需要Docker Client、Docker Server、Docker Daemon以及Docker Registry四者協同合作完成。本文從源碼的角度分析了四者各自的扮演的角色,分析過程中還涉及多種Docker概念,如repository、tag、TagStore、session、image、layer、image json、graph等。

8.作者介紹

孫宏亮,DaoCloud初創團隊成員,軟件工程師,浙江大學VLIS實驗室應屆研究生。讀研期間活躍在PaaS和Docker開源社區,對Cloud Foundry有深入研究和豐富實踐,擅長底層平臺代碼分析,對分布式平臺的架構有一定經驗,撰寫了大量有深度的技術博客。2014年末以合伙人身份加入DaoCloud團隊,致力于傳播以Docker為主的容器的技術,推動互聯網應用的容器化步伐。郵箱:allen.sun@daocloud.io

9.參考文獻

https://docs.docker.com/terms/image/

https://docs.docker.com/terms/layer

http://docs.studygolang.com/pkg/

轉載于:https://www.cnblogs.com/davidwang456/articles/9603036.html

總結

以上是生活随笔為你收集整理的Docker源码分析(十):Docker镜像下载的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

日韩黄色免费电影 | 伊人影院在线观看 | 大荫蒂欧美视频另类xxxx | 在线激情网 | 亚洲综合视频在线 | 三上悠亚在线免费 | 91在线视频 | 国产精品夜夜夜一区二区三区尤 | 成人免费在线播放视频 | 欧美精品亚洲二区 | 国产第一页在线观看 | 久久久久一区二区三区四区 | 欧美激情另类文学 | 日韩电影一区二区在线 | 黄色小说免费观看 | 久久久久久久亚洲精品 | 激情欧美在线观看 | 久久艹中文字幕 | 81国产精品久久久久久久久久 | 精品国产电影一区二区 | 日韩精选在线观看 | 免费麻豆视频 | 天天综合人人 | 日韩婷婷 | 五月婷婷激情综合网 | 99久久精品无码一区二区毛片 | 日本丶国产丶欧美色综合 | 日韩在线观看网站 | 麻豆果冻剧传媒在线播放 | 奇米网网址| 一区二区三区四区在线 | 九九99视频 | 亚洲综合狠狠干 | 亚洲精品xxxx | 久久精品国产亚洲精品2020 | 国产成人精品在线播放 | 久久国产精品一区二区三区四区 | 99久久久久成人国产免费 | 国产午夜精品一区二区三区在线观看 | 成人在线观看资源 | 又黄又爽的免费高潮视频 | 日本久久电影网 | 国产正在播放 | 久久精品国产亚洲精品 | 精品一区电影国产 | 日韩和的一区二在线 | 日日天天狠狠 | 国产亚洲片 | 91成人精品一区在线播放69 | 97精品国产一二三产区 | 亚洲婷婷伊人 | 黄色片毛片 | 97在线公开视频 | 91最新视频 | 国产字幕在线观看 | 国产精品淫片 | 亚洲美女精品区人人人人 | 天天操夜操| 一区二区高清在线 | 久久久精品网 | 欧美日韩国产伦理 | 日韩欧美专区 | 99久久久国产精品免费99 | 日韩免费在线观看视频 | 久久av观看 | 色激情五月 | 综合在线色 | 天天天天天操 | 中文字幕av全部资源www中文字幕在线观看 | www.99在线观看 | 亚洲福利精品 | 久久精品国产精品 | 亚洲高清不卡av | 国产一级在线播放 | 毛片3| 欧美精品亚洲二区 | 美女福利视频一区二区 | 91在线精品视频 | 国产精品毛片一区视频播不卡 | 九九九九免费视频 | 免费国产在线视频 | 精品一区电影 | 久久黄色小说 | av在线免费不卡 | www.午夜视频 | 黄色免费网站下载 | 日韩有码第一页 | avcom在线| 国产成人av片 | 亚洲精品一区二区18漫画 | 成人午夜黄色 | av网站在线观看免费 | 国产精品麻豆三级一区视频 | 亚洲一二三在线 | 国产伦理一区 | 成人av资源 | 亚洲1级片| 人人插人人插 | 久久99精品国产99久久 | 亚洲第一区在线观看 | 国产+日韩欧美 | 国产精品综合在线 | 一区二区视频电影在线观看 | 999精品| av高清一区 | 日韩精品2区| 激情欧美在线观看 | 久久精品视频在线免费观看 | 日韩av在线影视 | 黄视频色网站 | 51久久成人国产精品麻豆 | 久久久久亚洲精品国产 | 色婷婷在线播放 | 国产亚洲片 | 国产精品嫩草影院99网站 | 国产精品第7页 | 亚洲成人精品久久久 | 国产精品久久久久久久久久白浆 | 午夜视频在线网站 | 午夜电影 电影 | 国产专区在线视频 | 亚洲精品国产精品久久99 | 日韩久久久久久久久久 | 玖草影院 | 亚洲黄a | 国产在线一区二区 | 综合久久2023 | 永久中文字幕 | 性色av免费看| 西西444www大胆高清视频 | 精品免费视频 | 91av手机在线观看 | 99精品在线视频观看 | 中文字幕首页 | 男女拍拍免费视频 | 色av婷婷 | 99精品视频99 | 一区 二区 精品 | 天堂麻豆 | 欧美无极色 | 91精品蜜桃 | 伊人中文字幕在线 | 成人午夜久久 | 综合网五月天 | www五月天com| 一区二区三区高清在线观看 | 欧美日韩在线观看一区二区 | 欧美精品中文 | 在线免费中文字幕 | 亚洲精品国产欧美在线观看 | 尤物97国产精品久久精品国产 | 黄色毛片视频免费观看中文 | 久久久天堂 | 日韩专区av | 中文字幕在线视频国产 | 久久午夜电影网 | 精品久久久久久久久久久院品网 | 天天av资源 | 精品亚洲欧美无人区乱码 | 国产免费黄色 | 在线观看韩国av | 丁香激情五月 | 欧美成年网站 | 人成电影网| www久草| 97在线观看视频免费 | 特黄特黄的视频 | 欧美色婷 | va视频在线观看 | 99日韩精品 | 人人讲| 69亚洲乱| 欧美日韩视频观看 | 亚洲永久精品国产 | 九九激情视频 | 99热精品国产一区二区在线观看 | 久久中文字幕视频 | 日日夜夜天天射 | 69av在线播放 | 国产精品 中文在线 | 51久久夜色精品国产麻豆 | 伊人永久| 日韩中文字幕第一页 | 久久不射电影院 | 国产高清免费 | 色噜噜狠狠狠狠色综合久不 | 亚洲最新av网站 | 久久国产高清 | 国产精彩在线视频 | 日韩精品久久久免费观看夜色 | 久久久久国产精品午夜一区 | 国产午夜麻豆影院在线观看 | 蜜桃视频日韩 | 超碰九九 | 国产视频一区二区三区在线 | 亚洲天堂首页 | 成人v| 亚洲国产三级 | 亚州精品在线视频 | 五月天激情视频在线观看 | 最新色站| 久久成人免费电影 | 欧美成人在线免费观看 | 福利一区二区三区四区 | 成人网在线免费视频 | 免费观看成人网 | 91av免费看 | 成人欧美在线 | 久草热视频 | 成人免费观看在线视频 | 亚洲全部视频 | 99视频一区 | a亚洲视频| 国产黄色av网站 | 夜色.com | 国产一区二区三区视频在线 | 免费高清看电视网站 | 亚洲精品视频第一页 | 国产色婷婷精品综合在线手机播放 | 又色又爽的网站 | 亚洲成人av电影在线 | 日韩欧美电影在线 | 黄色精品视频 | 一区二区三区免费网站 | 在线观看视频一区二区 | 五月婷婷丁香综合 | 91成人精品一区在线播放69 | 91成人网在线播放 | 98超碰在线| 99精品视频免费在线观看 | 激情网综合 | 欧美日韩裸体免费视频 | 91九色porny在线 | 久久久一本精品99久久精品 | 视频直播国产精品 | 日韩网站中文字幕 | 不卡电影一区二区三区 | 麻豆一区在线观看 | 国产亚洲在线视频 | 欧美一区二区三区免费看 | 五月天久久婷 | 婷婷www | av在线官网 | 国产精品剧情在线亚洲 | 久久精品中文字幕少妇 | 99在线观看 | 欧美日韩精品免费观看视频 | 国产黄色资源 | 日韩在线免费视频观看 | 日日干av | 五月天婷亚洲天综合网精品偷 | 国产日韩精品在线观看 | 久久久久久久久久免费视频 | 久久成人国产精品免费软件 | www.久久爱.cn | 国产精品嫩草影院9 | 丰满少妇一级片 | 91九色蝌蚪视频在线 | 久久99精品久久久久婷婷 | 激情五月***国产精品 | 免费视频a | 日本免费久久高清视频 | 久久精品视频免费播放 | 丁香五月缴情综合网 | 国产日产高清dvd碟片 | 中文字幕一区二区三 | 97在线观看免费高清完整版在线观看 | 国产精品久久久久一区二区三区 | 国产精品v欧美精品v日韩 | 国产视频欧美视频 | 一区二区三区韩国免费中文网站 | 成人免费视频免费观看 | 九九九九九九精品任你躁 | 久久国产成人午夜av影院宅 | 国产电影黄色av | 久草99 | 四虎国产免费 | 亚洲国产欧美一区二区三区丁香婷 | 亚洲黄色免费在线 | 91精品久久久久久久久久入口 | 亚洲无吗天堂 | 91精品免费在线视频 | 欧美亚洲精品在线观看 | 久久99久久99精品免观看软件 | 午夜视频在线观看一区二区三区 | 深夜国产福利 | 免费在线观看视频a | 国产精品久久久久久久久婷婷 | 久久国产精品视频观看 | 国产经典av| 精品国产亚洲一区二区麻豆 | 亚洲国产操 | 日产乱码一二三区别免费 | www.久热 | 成人黄色在线 | 中文字幕免费成人 | 黄色影院在线免费观看 | 黄色国产成人 | 亚洲永久国产精品 | 日本黄色一级电影 | 又黄又爽又湿又无遮挡的在线视频 | 久久久久伦理电影 | 成人黄色小说网 | 久草在线视频免赞 | 国产成人一区二区精品非洲 | 人人爽人人搞 | 午夜电影久久 | 四虎永久网站 | 视频在线在亚洲 | 九九久久国产精品 | 亚洲黄色一级大片 | 国产精品成人久久 | 四虎精品成人免费网站 | 欧美精品xxx | 久热香蕉视频 | 亚洲国产影院av久久久久 | 激情视频区| 五月婷综合 | 国产成人久久av977小说 | a爱爱视频 | 91精品无人成人www | 国产精品久久在线 | 黄视频网站大全 | 天天操狠狠操 | 亚洲高清久久久 | 色婷婷亚洲婷婷 | 一区二区三区视频在线 | 狠狠精品 | 久久黄色网址 | 免费看一级片 | 美女精品久久久 | 国内综合精品午夜久久资源 | 视色网站 | 国产亚洲小视频 | 日日干 天天干 | 日韩精品一区二区三区电影 | 亚洲国产日韩欧美在线 | 中文字幕免费中文 | 日韩美视频| 天天色天天色 | 久草在线99| 在线а√天堂中文官网 | 精品国产乱码一区二区三区在线 | 一区二区三区四区不卡 | 午夜精品久久久久久久99水蜜桃 | 超碰在线91| 亚洲人人爱 | 久热国产视频 | 国产成人在线网站 | 日本三级在线观看中文字 | 99精品观看 | 黄色免费观看 | 中国一级片在线观看 | 久艹在线播放 | 天天躁天天躁天天躁婷 | 欧美日韩国产一区二区三区 | 超碰97人人射妻 | 91免费视频网站在线观看 | 久久成人精品电影 | 日韩丝袜| 久久精品国产亚洲 | www亚洲精品 | 久久久久久久久久影院 | 国产精品影音先锋 | 一区二区三区国产精品 | 欧美日韩3p | 欧美激情精品久久 | 国产亚洲精品福利 | 亚洲欧洲国产视频 | 中文一区二区三区在线观看 | 欧美精品免费在线 | 亚洲午夜精品福利 | 最近乱久中文字幕 | 岛国一区在线 | 亚洲国产精品成人综合 | 久久a级片 | 免费高清在线视频一区· | av在线播放观看 | 毛片网站在线观看 | 99久久婷婷国产精品综合 | 黄色大片免费播放 | 亚洲综合在线视频 | 国产人免费人成免费视频 | 在线不卡的av | 日日操天天爽 | 色婷婷综合久久久久 | 国产高清av | 久久精彩 | 国产精品 日韩 | 色综合天天综合网国产成人网 | 成全在线视频免费观看 | 久久久香蕉视频 | 天天超碰| 久久99精品久久久久久清纯直播 | 国产精品免费高清 | 国产精品第一页在线观看 | 正在播放国产一区 | 中文在线a√在线 | 成人黄色大片在线观看 | 一级免费观看 | 丁香视频 | 就操操久久 | 日韩簧片在线观看 | 国产精品久久久久久电影 | 日日弄天天弄美女bbbb | www.888.av | 在线观看视频一区二区三区 | www.午夜色.com | 中文在线8资源库 | 国产在线观看一区 | 最新中文字幕视频 | 国产成人三级在线观看 | 波多野结衣电影一区二区三区 | 成人一级电影在线观看 | 成人一级 | 亚洲精品久久久蜜桃直播 | av官网在线| 久久综合激情 | 欧美ⅹxxxxxx | 在线韩国电影免费观影完整版 | 亚洲欧美激情精品一区二区 | 91九色视频在线播放 | 麻豆视频在线观看 | av黄色成人| 国产精在线 | 99热最新地址| 亚洲国产视频a | 日韩中文字幕国产 | 国产高清亚洲 | 最近中文字幕高清字幕免费mv | 久久精品一区二区三区四区 | www.久久精品视频 | 天天操天天操天天操天天 | 精品久久在线 | 九九久久电影 | www麻豆视频 | 黄色三级网站 | 日韩高清在线看 | 在线亚洲激情 | 亚洲精品国产精品国 | 在线免费观看视频一区二区三区 | 激情av网 | 国产免费观看久久 | 国产中文字幕网 | 狠狠躁日日躁夜夜躁av | 综合激情网| 一区二区 不卡 | 日韩av影片在线观看 | 国产亚洲字幕 | 亚洲最大免费成人网 | 香蕉影视app | 人人射人人爱 | 一区二区电影在线观看 | 国产明星视频三级a三级点| 成人羞羞免费 | 黄a网| 欧美精品在线观看 | 精品美女国产在线 | 93久久精品日日躁夜夜躁欧美 | 免费看一级黄色大全 | 国产三级久久久 | 中文字幕中文字幕中文字幕 | 成年人av在线播放 | 波多野结衣网址 | 在线a人片免费观看视频 | 91麻豆福利| 午夜久久影院 | 在线中文字幕播放 | 成人黄色在线观看视频 | 久久人人爽人人片 | 亚洲欧美视频在线播放 | 综合婷婷丁香 | 外国av网| 亚洲 欧美 91| 粉嫩av一区二区三区四区在线观看 | 黄网站app在线观看免费视频 | 色小说在线 | 成年人在线播放视频 | 免费av 在线 | 黄色国产成人 | 国产精品一区二区三区视频免费 | 九九视频在线播放 | 久久尤物电影视频在线观看 | 综合网五月天 | 久久在视频 | 精品久久久久久电影 | 五月婷婷中文网 | 97超碰在线免费 | 欧美一级片 | 国产美女视频网站 | 超碰97在线资源站 | 久久毛片高清国产 | 手机av片| 91免费版在线| 久久伦理 | 人人涩 | 亚洲一区久久 | 91久久爱热色涩涩 | 国产精品第2页 | 亚洲人视频在线 | 亚洲精品中文字幕视频 | 久久艹国产| 精品一区 在线 | 不卡av电影在线观看 | 久久综合给合久久狠狠色 | 久久精品99国产国产 | 亚洲 欧美 综合 在线 精品 | 黄色成人在线网站 | 国产精品日韩久久久久 | 丁香婷婷色月天 | 91视频 - v11av | 毛片基地黄久久久久久天堂 | av片一区二区 | 成人国产在线 | 免费在线电影网址大全 | 日韩视频1区 | 国产精品麻豆视频 | 91在线看黄 | 国产一区国产精品 | 国产精品久久久久久久免费 | 国产美腿白丝袜足在线av | 人人玩人人添人人澡97 | 一本色道久久综合亚洲二区三区 | 国产精品久久久久免费观看 | 欧美va在线观看 | 久久综合操 | 2023年中文无字幕文字 | 国产精品乱码高清在线看 | 国产手机在线观看视频 | 国产精品视频地址 | 青草视频在线看 | 精品在线不卡 | 久久人人爽视频 | 亚洲国产精品成人女人久久 | 精品视频在线观看 | 99re国产 | av 一区二区三区 | 久久精品亚洲国产 | 日日夜夜骑 | 黄色高清视频在线观看 | 国产在线a | 视频在线在亚洲 | 97视频亚洲 | 在线看av的网址 | 日韩天天干| 干av在线 | 亚洲精品国产精品乱码不99热 | 福利视频导航网址 | 麻豆91精品视频 | 黄色一级在线视频 | 精品免费视频. | 国语麻豆| 国产精品自产拍在线观看 | 亚洲婷婷丁香 | 亚洲 欧洲 国产 精品 | 色综合久久五月天 | 黄色特一级 | 日韩色在线观看 | 日韩极品在线 | 欧美综合在线视频 | 涩涩网站在线看 | 日韩三级中文字幕 | 5月丁香婷婷综合 | 国产视频手机在线 | 91精品国产综合久久福利不卡 | 国产麻豆剧果冻传媒视频播放量 | 日韩免费观看一区二区 | 美女视频黄是免费的 | 97免费| 亚洲 欧美 日韩 综合 | 美女视频久久黄 | 久久蜜桃av | 国产视频 亚洲精品 | 久久99亚洲精品久久 | 91免费高清| 成 人 免费 黄 色 视频 | 伊人射 | 国产免费区 | 欧美精品一二 | 99在线观看精品 | 久久精品123| 成人丝袜 | 欧洲精品久久久久毛片完整版 | 国产精品岛国久久久久久久久红粉 | 中国一级特黄毛片大片久久 | 午夜久久美女 | 天天干天天碰 | 日韩亚洲在线观看 | 日韩欧美xxxx | 日韩精品免费一区二区 | 国产97在线播放 | 日本三级在线观看中文字 | 日韩动漫免费观看高清完整版在线观看 | 亚洲在线激情 | 一区二区三区四区精品 | 日韩免费b | 国产一级免费视频 | 欧美日韩高清一区二区三区 | www日韩在线| av成人动漫在线观看 | av在线直接看 | 天海翼一区二区三区免费 | 国产精品无 | 国产精品免费成人 | 成人在线视频一区 | 国产在线自 | 人交video另类hd | 国产一区二区精品在线 | 91中文字幕在线观看 | 精品一区二区综合 | 国产成人久久精品亚洲 | 亚洲在线成人精品 | 久久婷婷综合激情 | 日韩精品久久久久久久电影99爱 | 在线观看日韩精品视频 | av在线等| 国产色拍拍拍拍在线精品 | 狠狠干免费 | 婷婷在线资源 | 四虎最新域名 | 日韩三级免费 | 久久影视中文字幕 | 在线国产激情视频 | 中文视频一区二区 | 日韩成人中文字幕 | 国产色妞影院wwwxxx | 在线免费观看羞羞视频 | 激情视频一区二区三区 | av电影中文字幕在线观看 | 国模精品在线 | 久久精品国产亚洲 | 99精品免费久久久久久日本 | 精品久久久久久亚洲综合网站 | 国产日韩视频在线播放 | 亚洲免费精品一区二区 | 深夜精品福利 | 久爱精品在线 | 国产视频日韩视频欧美视频 | 人人爽人人爽人人片av免 | 超碰日韩 | 天天天综合 | 91精品国产网站 | 日韩av手机在线看 | 69av在线视频 | 玖玖综合网 | 在线观看亚洲 | 91九色蝌蚪在线 | 天天插天天 | 色婷婷福利 | 亚洲精品乱码久久久一二三 | www.天天干.com | 免费日韩一区二区 | 免费看久久久 | 亚洲做受高潮欧美裸体 | 久久精品中文字幕一区二区三区 | 在线观看片 | 国产精品人人做人人爽人人添 | 国产亚洲精品久久久久久移动网络 | 日韩网站一区二区 | 国产亚洲精品精品精品 | 亚洲 中文字幕av | 免费能看的黄色片 | 国产精品美女久久久网av | 国产原创在线视频 | 在线观看国产一区二区 | 日韩精品视频在线观看网址 | 黄色av电影在线观看 | 最新高清无码专区 | av电影亚洲| 人人插人人 | 久久婷婷五月综合色丁香 | 开心激情婷婷 | 日韩乱码在线 | 亚洲精品乱码久久久久久蜜桃欧美 | 欧美国产一区在线 | 亚洲四虎| 国产精品网红直播 | 亚洲精品久久久久久久不卡四虎 | 久久tv视频 | 亚洲精品乱码久久久久久写真 | 日韩欧美一区二区三区黑寡妇 | 99精品欧美一区二区 | 午夜精品99久久免费 | 天堂在线视频免费观看 | 999一区二区三区 | 99精品在这里 | 国产精品乱码高清在线看 | 丁香六月天婷婷 | aⅴ精品av导航 | 香蕉久久久久久av成人 | 亚洲无吗天堂 | 中日韩男男gay无套 日韩精品一区二区三区高清免费 | 欧美日韩在线精品一区二区 | 四虎永久精品在线 | 午夜黄色大片 | 永久av免费在线观看 | 亚洲 中文字幕av | 国产精品一区专区欧美日韩 | 丁香视频全集免费观看 | 一区二区欧美在线观看 | 国产精品久久久久久久久久99 | 成人免费观看电影 | 综合在线亚洲 | 成人中文字幕在线 | 欧美另类z0zx | 成人影视免费看 | 国产精品白浆视频 | 欧美日产在线观看 | 日韩 在线观看 | 亚洲日本va中文字幕 | 一区二区三区在线视频111 | 在线观看中文字幕一区 | 久久久国产精品亚洲一区 | 五月婷婷爱 | 中文国产在线观看 | 毛片黄色一级 | 日韩av午夜| 亚洲国产电影在线观看 | www五月天com| 国产精品免费观看网站 | 亚洲精品在 | 日韩电影一区二区在线 | 亚洲精品美女久久久久 | 久久公开视频 | av大全在线免费观看 | 91精品婷婷国产综合久久蝌蚪 | 久久露脸国产精品 | 免费www视频| 中文日韩在线 | 98超碰在线 | 天堂在线一区二区 | 精品久久久久久亚洲 | 美女网站免费福利视频 | 天天操天天色综合 | a一片一级| www看片网站| 午夜精品久久久久久久爽 | 视频在线观看亚洲 | av在线小说 | 欧美一区二区在线 | 亚洲视频在线免费看 | 美女免费视频一区 | www.夜夜爱| 91在线看免费 | 久草在线久草在线2 | 国产一二三四在线观看视频 | 精品伦理一区二区三区 | a黄色片 | 国产精品手机播放 | 日本中文字幕在线电影 | 四虎4hu永久免费 | 久久一级片 | 免费在线观看中文字幕 | av在线免费网| 玖操| 狠狠操狠狠干2017 | 99视频久久| 九九在线免费视频 | 中文字幕成人网 | 新版资源中文在线观看 | 在线一区二区三区 | 亚洲另类视频在线观看 | 国内精品久久久久久久久久 | 日韩av一区在线观看 | 日本精品一 | 亚洲乱码国产乱码精品天美传媒 | 最近日本韩国中文字幕 | 亚洲精品合集 | 中文字幕中文字幕在线中文字幕三区 | 色综合久久66 | 激情五月婷婷丁香 | 69久久久久久久 | 久草在线视频看看 | 天天操比| 日韩欧美视频在线 | 久精品一区 | 网站免费黄 | 国产人成看黄久久久久久久久 | 黄色av一区二区三区 | 国产一级不卡视频 | 国产精品久久一区二区三区不卡 | 最新一区二区三区 | 99热这里只有精品免费 | 成人啊 v | 欧美另类高清 | 最近免费中文字幕 | 99热在线国产精品 | 色综合久久精品 | 天天色天天综合 | 亚洲精品视频免费 | 国产视频在线观看一区二区 | 日韩精品欧美一区 | 欧美精品你懂的 | 久久99精品一区二区三区三区 | 97超碰色 | 99久久精品国 | 中文字幕免费高清在线 | 久久香蕉国产精品麻豆粉嫩av | 国产精品成人国产乱一区 | 手机成人在线电影 | 欧洲亚洲女同hd | 国产中文字幕视频在线 | 99久久电影 | 国产九九精品 | 国产高清精品在线观看 | 九九热在线视频 | 久久久久99精品成人片三人毛片 | 综合网久久 | 国产亚洲精品久久久久动 | 手机在线看a | 国产在线精品一区二区不卡了 | 超碰97免费在线 | 国产亚洲精品电影 | 2023国产精品自产拍在线观看 | www.狠狠插.com | 91插插插免费视频 | 亚洲另类xxxx| 在线观看国产成人av片 | 国产精品精品国产 | 高清视频一区二区三区 | 手机看片1042 | 久久免费视频国产 | 六月天综合网 | 91精品在线免费视频 | www.人人草 | 成人免费视频网址 | 久免费| 精品国产一区二区三区不卡 | 国产裸体永久免费视频网站 | 在线午夜 | www.色com| 一区二区三区四区在线 | 免费看片黄色 | 日本久久久亚洲精品 | 亚洲黄色网络 | 99欧美精品 | 久久午夜精品视频 | 国产精品一级视频 | 欧美精品中文在线免费观看 | 91精品国产成人观看 | 狠狠干天天操 | 日韩色综合 | 中文字幕电影在线 | 久久综合久久综合久久 | 日韩a在线观看 | 亚洲精品在线免费播放 | av高清影院 | 国产精品18久久久久vr手机版特色 | 亚洲国产剧情 | 免费欧美精品 | 久久久久女人精品毛片 | 婷婷视频在线播放 | 日本精品久久久久影院 | 婷婷综合视频 | 精品乱码一区二区三四区 | 色综合欧洲 | 九九热在线视频 | 中文字幕在线专区 | 麻豆视频在线免费观看 | 国产精品久久久久久久毛片 | 99久久国产免费看 | 97精品视频在线 | 久久综合精品一区 | 韩日精品中文字幕 | 国产精品久久久久毛片大屁完整版 | 欧美精品亚洲精品日韩精品 | 国产二区精品 | 久久综合狠狠狠色97 | 激情网五月 | 日韩av播放在线 | 在线观看涩涩 | 国产精品免费观看国产网曝瓜 | 一区二区三区中文字幕在线观看 | 国产一级片网站 | 毛片www | 日韩电影精品 | 精品视频国产一区 | 婷婷激情5月天 | 欧美日韩高清一区二区 国产亚洲免费看 | 欧美另类激情 | 午夜国产影院 | 网站在线观看你们懂的 | 亚洲精品国偷拍自产在线观看蜜桃 | 欧美日韩亚洲精品在线 | 国产欧美在线一区 | 午夜精品一区二区三区在线 | 亚洲视频免费在线观看 | 97超级碰 | 九九热99视频 | 91丝袜美腿 | 欧美精品少妇xxxxx喷水 | 九九久久久久久久久激情 | 亚洲国产久 | 久久国产精品99国产精 | 在线观看a视频 | 999国产| 久久精品视频观看 | 麻豆一区二区 | 欧美精品视 | 国产欧美精品一区二区三区 | 日本大片免费观看在线 | 久久精品美女视频网站 | 欧美一区二区三区在线看 | 亚洲成人xxx | 91在线国内视频 | 亚洲精品人人 | 69绿帽绿奴3pvideos | 久久精品5 | 国产一区二区在线播放视频 | 国产精品成人免费一区久久羞羞 | 射久久久 | 99精品国产兔费观看久久99 | 国内精品一区二区 | 中文字幕高清在线播放 | 久久视频精品在线 | 又色又爽又黄高潮的免费视频 | 欧美日韩高清一区二区三区 | 亚洲无吗视频在线 | 蜜臀av一区二区 | 一区二区视频电影在线观看 | 天天插天天操天天干 | 91探花在线| 久久伊人八月婷婷综合激情 | 黄色精品久久久 | 国产精品99免费看 | 天天透天天插 | 黄色一级大片在线免费看产 | 日韩精品免费 | 精品国产一区二区三区在线观看 | 天天久久夜夜 | 国产精品久久久久国产精品日日 | 亚洲精品在线一区二区 | 日韩网站在线看片你懂的 | 国产最新视频在线 | 国产精品免费成人 | av丁香| 黄色电影网站在线观看 | 激情av在线资源 | 中文字幕av电影下载 | 午夜视频在线观看一区二区三区 | 精品国产一区二区三区久久 | 99精品久久99久久久久 | 国产午夜精品免费一区二区三区视频 | 成人在线免费观看网站 | 97精品久久| 国产一区在线观看视频 | 日韩av伦理片 | 亚洲精品66| av资源免费在线观看 | 久久国语 | 午夜视频在线观看一区二区三区 | 亚洲精品在线资源 | 亚洲四虎在线 | 欧美日韩国产一区二区三区在线观看 | 久久久观看 | 亚洲精品美女在线 | 亚洲精品永久免费视频 | 九九免费在线观看视频 | 色多多污污在线观看 | 国产福利午夜 | 在线国产一区二区 | 亚洲一区二区三区在线看 | 中文字幕乱偷在线 | 国产成人久 | 一级一片免费看 | 天堂在线一区 | 久久五月天色综合 | 8x成人免费视频 | 日本久久99 | 在线观看91精品国产网站 | 亚洲精品88欧美一区二区 | 久久中文网 | 国产综合香蕉五月婷在线 | 黄色小说18 | 亚洲午夜大片 | 亚洲精品久久久久58 | 五月天中文字幕mv在线 | 欧美aa一级片 | 色久av| 狠狠地操 | 免费看污片 | 国产亚洲婷婷免费 | 精品国产乱码久久久久 | 999成人网 | 中文在线a√在线 | 国产精品福利在线 | 免费看十八岁美女 | 中文免费在线观看 | av高清一区| 亚洲精品大片www | 亚洲免费视频在线观看 | 久久精品一区二区三区国产主播 | 成年人看片网站 | 久久久国产精品一区二区三区 | 欧美 日韩精品 | 五月天婷婷免费视频 | 91精品视频播放 | 99久久激情|