1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【效率】Docker:從入門到實(shí)戰(zhàn)過程全記錄

        共 29307字,需瀏覽 59分鐘

         ·

        2022-05-11 05:26

        和Docker相關(guān)的概念

        想要真正理解Docker,就不得不從虛擬化技術(shù)的發(fā)展歷程說起。普遍認(rèn)為虛擬化技術(shù)經(jīng)歷了物理機(jī)時代、虛擬機(jī)時代,目前已經(jīng)進(jìn)入到了容器化時代。可以說,Docker是虛擬化技術(shù)不斷發(fā)展的必然結(jié)果。

        那么,什么是容器呢?容器和虛擬機(jī)有什么不同?Docker和容器又是什么關(guān)系呢?搞明白這幾個問題,Docker的概念就清晰了。

        1.1 虛擬機(jī)和容器

        借助于VMWare等軟件,可以在一臺計算機(jī)上創(chuàng)建多個虛擬機(jī),每個虛擬機(jī)都擁有獨(dú)立的操作系統(tǒng),可以各自獨(dú)立的運(yùn)行程序。這種分身術(shù)雖然隔離度高(操作系統(tǒng)級),使用方便(類似物理機(jī)),但占用存儲資源多(GB級)、啟動速度慢(分鐘級)的缺點(diǎn)也是顯而易見的。

        相較于虛擬機(jī),容器(Container)是一種輕量型的虛擬化技術(shù),它虛擬的是最簡運(yùn)行環(huán)境(類似于沙盒)而非操作系統(tǒng),啟動速度快(秒級)、占用存儲資源少(KB級或MB級),容器間隔離度為進(jìn)程級。在一臺計算機(jī)上可以運(yùn)行上千個容器,這是容器技術(shù)對虛擬機(jī)的碾壓式優(yōu)勢。

        1.2 容器、鏡像和Docker

        Docker是一個開源的應(yīng)用容器引擎,可以創(chuàng)建容器以及基于容器運(yùn)行的程序。Docker可以讓開發(fā)者打包他們的應(yīng)用和依賴包到一個輕量級、可移植的容器中,然后發(fā)布到任何流行的Linux機(jī)器上,也可以實(shí)現(xiàn)虛擬化。

        聽起來很簡單,但是在Docker和容器之間,還隱藏著一個鏡像的概念,令初學(xué)者頗感困惑。本質(zhì)上,Docker鏡像是一個特殊的文件系統(tǒng),它提供容器運(yùn)行時所需的程序、庫、資源、配置等文件。Docker鏡像類似于一個py文件,它需要Docker的運(yùn)行時(類似于Python解釋器)運(yùn)行。鏡像被運(yùn)行時,即創(chuàng)建了一個鏡像的實(shí)例,一個實(shí)例就是一個容器。

        1.3 Docker 和 k8s

        作為容器引擎,Docker為容器化的應(yīng)用程序提供了開放的標(biāo)準(zhǔn),使得開發(fā)者可以用管理應(yīng)用程序的方式來管理基礎(chǔ)架構(gòu),實(shí)現(xiàn)快速交付、測試和部署代碼。隨著容器的大量使用,又產(chǎn)生了如何協(xié)調(diào)、調(diào)度和管理容器的問題,Docker的容器編排應(yīng)運(yùn)而生。

        k8s是Google開源的一個容器編排引擎,它支持自動化部署、大規(guī)模可伸縮、應(yīng)用容器化管理,是一個開源的,用于管理云平臺中多個主機(jī)上的容器化的應(yīng)用,k8s的目標(biāo)是讓部署容器化的應(yīng)用簡單并且高效,k8s提供了應(yīng)用部署、規(guī)劃、更新、維護(hù)的一種機(jī)制。

        Docker和k8sr都是以containerd(容器化標(biāo)準(zhǔn))作為運(yùn)行時,因此使用Docker創(chuàng)建的鏡像完全可以在k8s中無障礙的使用。

        Docker的安裝

        2.1 在ubuntu中安裝

        在linux系統(tǒng)中安裝Docker非常簡單,官方為我們提供了一鍵安裝腳本。這個方法也適用于Debian或CentOS等發(fā)行版。

        curl?-sSL?https://get.daocloud.io/docker?|?sh

        安裝過程如果出現(xiàn)超時,不要灰心,多試幾次,總會成功的。安裝完成后,Docker只能被root用戶使用,可以使用下面的命令取消權(quán)限限制:

        sudo?gpasswd?-a?<你的用戶名>?docker

        然后,重啟docker服務(wù):

        sudo?service?docker?restart

        最后,關(guān)閉當(dāng)前的命令行,重新打開新的命令行就可以了。

        順便提一下,如果在CentOS下安裝,可能會出現(xiàn)一堆類似于下面的錯誤:

        問題?1:?problem?with?installed?package?podman-2.0.5-5.module_el8.3.0+512+b3b58dca.x86_64
        ??-?package?podman-2.0.5-5.module_el8.3.0+512+b3b58dca.x86_64?requires?runc?>=?1.0.0-57,?but?none?of?the?providers?can?be?installed
        ??-?package?podman-3.0.1-6.module_el8.4.0+781+acf4c33b.x86_64?requires?runc?>=?1.0.0-57,?but?none?of?the?providers?can?be?installed
        ??-?package?podman-3.0.1-7.module_el8.4.0+830+8027e1c4.x86_64?requires?runc?>=?1.0.0-57,?but?none?of?the?providers?can?be?installed
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-68.rc92.module_el8.3.0+475+c50ce30b.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-68.rc92.module_el8.3.0+475+c50ce30b.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-70.rc92.module_el8.4.0+673+eabfc99d.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-70.rc92.module_el8.4.0+673+eabfc99d.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-73.rc93.module_el8.4.0+830+8027e1c4.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-73.rc93.module_el8.4.0+830+8027e1c4.x86_64
        ??-?cannot?install?the?best?candidate?for?the?job
        ??-?package?runc-1.0.0-64.rc10.module_el8.4.0+522+66908d0c.x86_64?is?filtered?out?by?modular?filtering
        ??-?package?runc-1.0.0-65.rc10.module_el8.4.0+819+4afbd1d6.x86_64?is?filtered?out?by?modular?filtering
        ??-?package?runc-1.0.0-70.rc92.module_el8.4.0+786+4668b267.x86_64?is?filtered?out?by?modular?filtering
        ??-?package?runc-1.0.0-71.rc92.module_el8.4.0+833+9763146c.x86_64?is?filtered?out?by?modular?filtering
        ?問題?2:?package?podman-3.0.1-6.module_el8.4.0+781+acf4c33b.x86_64?requires?runc?>=?1.0.0-57,?but?none?of?the?providers?can?be?installed
        ??-?package?containerd.io-1.4.3-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-70.rc92.module_el8.4.0+673+eabfc99d.x86_64
        ??-?package?containerd.io-1.4.3-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-70.rc92.module_el8.4.0+673+eabfc99d.x86_64
        ??-?package?containerd.io-1.4.3-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-73.rc93.module_el8.4.0+830+8027e1c4.x86_64
        ??-?package?containerd.io-1.4.3-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-73.rc93.module_el8.4.0+830+8027e1c4.x86_64
        ??-?package?docker-ce-3:20.10.7-3.el8.x86_64?requires?containerd.io?>=?1.4.1,?but?none?of?the?providers?can?be?installed
        ??-?package?containerd.io-1.4.3-3.2.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-70.rc92.module_el8.4.0+673+eabfc99d.x86_64
        ??-?package?containerd.io-1.4.3-3.2.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-70.rc92.module_el8.4.0+673+eabfc99d.x86_64
        ??-?package?containerd.io-1.4.3-3.2.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-73.rc93.module_el8.4.0+830+8027e1c4.x86_64
        ??-?package?containerd.io-1.4.3-3.2.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-73.rc93.module_el8.4.0+830+8027e1c4.x86_64
        ??-?package?podman-catatonit-3.0.1-6.module_el8.4.0+781+acf4c33b.x86_64?requires?podman?=?3.0.1-6.module_el8.4.0+781+acf4c33b,?but?none?of?the?providers?can?be?installed
        ??-?problem?with?installed?package?podman-catatonit-2.0.5-5.module_el8.3.0+512+b3b58dca.x86_64
        ??-?package?podman-catatonit-3.0.1-7.module_el8.4.0+830+8027e1c4.x86_64?requires?podman?=?3.0.1-7.module_el8.4.0+830+8027e1c4,?but?none?of?the?providers?can?be?installed
        ??-?package?podman-3.0.1-7.module_el8.4.0+830+8027e1c4.x86_64?requires?runc?>=?1.0.0-57,?but?none?of?the?providers?can?be?installed
        ??-?package?containerd.io-1.4.3-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-68.rc92.module_el8.3.0+475+c50ce30b.x86_64
        ??-?package?containerd.io-1.4.3-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-68.rc92.module_el8.3.0+475+c50ce30b.x86_64
        ??-?package?containerd.io-1.4.3-3.2.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-68.rc92.module_el8.3.0+475+c50ce30b.x86_64
        ??-?package?containerd.io-1.4.3-3.2.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-68.rc92.module_el8.3.0+475+c50ce30b.x86_64
        ??-?package?containerd.io-1.4.4-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-68.rc92.module_el8.3.0+475+c50ce30b.x86_64
        ??-?package?containerd.io-1.4.4-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-68.rc92.module_el8.3.0+475+c50ce30b.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-68.rc92.module_el8.3.0+475+c50ce30b.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-68.rc92.module_el8.3.0+475+c50ce30b.x86_64
        ??-?cannot?install?the?best?candidate?for?the?job
        ??-?package?runc-1.0.0-64.rc10.module_el8.4.0+522+66908d0c.x86_64?is?filtered?out?by?modular?filtering
        ??-?package?runc-1.0.0-65.rc10.module_el8.4.0+819+4afbd1d6.x86_64?is?filtered?out?by?modular?filtering
        ??-?package?runc-1.0.0-70.rc92.module_el8.4.0+786+4668b267.x86_64?is?filtered?out?by?modular?filtering
        ??-?package?runc-1.0.0-71.rc92.module_el8.4.0+833+9763146c.x86_64?is?filtered?out?by?modular?filtering
        ??-?package?containerd.io-1.4.4-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-70.rc92.module_el8.4.0+673+eabfc99d.x86_64
        ??-?package?containerd.io-1.4.4-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-70.rc92.module_el8.4.0+673+eabfc99d.x86_64
        ??-?package?containerd.io-1.4.4-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-73.rc93.module_el8.4.0+830+8027e1c4.x86_64
        ??-?package?containerd.io-1.4.4-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-73.rc93.module_el8.4.0+830+8027e1c4.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-70.rc92.module_el8.4.0+673+eabfc99d.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-70.rc92.module_el8.4.0+673+eabfc99d.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?conflicts?with?runc?provided?by?runc-1.0.0-73.rc93.module_el8.4.0+830+8027e1c4.x86_64
        ??-?package?containerd.io-1.4.6-3.1.el8.x86_64?obsoletes?runc?provided?by?runc-1.0.0-73.rc93.module_el8.4.0+830+8027e1c4.x86_64
        ??-?package?podman-catatonit-2.0.5-5.module_el8.3.0+512+b3b58dca.x86_64?requires?podman?=?2.0.5-5.module_el8.3.0+512+b3b58dca,?but?none?of?the?providers?can?be?installed
        ??-?package?podman-2.0.5-5.module_el8.3.0+512+b3b58dca.x86_64?requires?runc?>=?1.0.0-57,?but?none?of?the?providers?can?be?installed

        這是由于docker和Podman沖突造成的,需要先卸載Podman:

        yum?erase?podman?buildah

        2.2 在Win10中安裝

        Docker的運(yùn)行,依賴linux的環(huán)境,官方提供了Docker Desktop for Windows,但是它需要安裝Hyper-V,Hyper-V是微軟開發(fā)的虛擬機(jī),類似于 VMWare 或 VirtualBox,僅適用于 Windows 10。這個虛擬機(jī)一旦啟用,QEMU、VirtualBox 或 VMWare Workstation 15 及以下版本將無法使用!如果你必須在電腦上使用其他虛擬機(jī)(例如開發(fā) Android 應(yīng)用必須使用的模擬器),請不要使用 Hyper-V!

        我的電腦是win10家庭版,不能直接安裝hyper-v,需要將下面的命令保存到cmd文件中:

        pushd?"%~dp0"

        dir?/b?%SystemRoot%\servicing\Packages\*Hyper-V*.mum?>hyper-v.txt

        for?/f?%%i?in?('findstr?/i?.?hyper-v.txt?2^>nul')?do?dism?/online?/norestart?/add-package:"%SystemRoot%\servicing\Packages\%%i"

        del?hyper-v.txt

        Dism?/online?/enable-feature?/featurename:Microsoft-Hyper-V-All?/LimitAccess?/ALL

        然后在cmd文件上點(diǎn)擊右鍵,選擇使用管理員運(yùn)行。執(zhí)行完畢后會重啟,在重啟的過程中進(jìn)行安裝。

        2.3 Hello world

        docker服務(wù)啟動的情況下,運(yùn)行下面的命令:

        docker?run?ubuntu:20.04?/bin/echo?"Hello?world"

        此命令的含義是:

        • docker run:運(yùn)行docker鏡像命令

        • ubuntu:20.04:鏡像名稱為ubuntu版本號為20.04

        • /bin/echo “Hello world”:運(yùn)行參數(shù),此鏡像的參數(shù)含義為運(yùn)行鏡像的echo命令顯示hello world

        第一次運(yùn)行時,因?yàn)楸镜貨]有ubuntu:20.04鏡像,docker會自動從鏡像服務(wù)器下載。下載過程可能需要多試幾次,只要成功一次,以后執(zhí)行就不再需要下載了。

        docker官方還提供了一個hello-world鏡像,可以直接運(yùn)行:

        docker?run?hello-world

        此命令省略了鏡像版本和運(yùn)行參數(shù),docker使用latest作為版本,即最新版本。

        從hello world的例子中,也可以體驗(yàn)到,docker實(shí)例的運(yùn)行是非??斓摹?/p>

        Docker鏡像的使用

        docker官方的鏡像庫比較慢,在進(jìn)行鏡像操作之前,需要將鏡像源設(shè)置為國內(nèi)的站點(diǎn)。

        新建文件/etc/docker/daemon.json,輸入如下內(nèi)容:

        {
        ????"registry-mirrors"?:?[
        ????????"https://registry.docker-cn.com",
        ????????"https://docker.mirrors.ustc.edu.cn",
        ????????"http://hub-mirror.c.163.com",
        ????????"https://cr.console.aliyun.com/"
        ????]
        }

        然后重啟docker的服務(wù):

        systemctl?restart?docker

        3.1 列出本地所有鏡像

        執(zhí)行命令 docker images 可以查看

        $?docker?images
        REPOSITORY??????????TAG?????????????????IMAGE?ID????????????CREATED?????????????SIZE
        ubuntu??????????????20.04???????????????f643c72bc252????????5?weeks?ago?????????72.9MB
        hello-world?????????latest??????????????bf756fb1ae65????????12?months?ago???????13.3kB

        當(dāng)前我本地只有剛才安裝的兩個鏡像。

        3.2 從鏡像庫中查找鏡像

        執(zhí)行命令 docker search 鏡像名稱可以從docker鏡像庫中查找鏡像。

        $?docker?search?python
        NAME?????????????????????????????DESCRIPTION?????????????????????????????????????STARS??????????????OFFICIAL??????????AUTOMATED
        python???????????????????????????Python?is?an?interpreted,?interactive,?objec…???5757????????????????[OK]????????????????
        django???????????????????????????Django?is?a?free?web?application?framework,?…???1039????????????????[OK]????????????????
        pypy?????????????????????????????PyPy?is?a?fast,?compliant?alternative?implem…???260?????????????????[OK]????????????????
        joyzoursky/python-chromedriver???Python?with?Chromedriver,?for?running?automa…???57??????????????????????????????????????[OK]
        nikolaik/python-nodejs???????????Python?with?Node.js?????????????????????????????57??????????????????????????????????????[OK]
        arm32v7/python???????????????????Python?is?an?interpreted,?interactive,?objec…???53??????????????????????????????????????
        circleci/python??????????????????Python?is?an?interpreted,?interactive,?objec…???42??????????????????????????????????????
        centos/python-35-centos7?????????Platform?for?building?and?running?Python?3.5…???38??????????????????????????????????????
        centos/python-36-centos7?????????Platform?for?building?and?running?Python?3.6…???30??????????????????????????????????????
        hylang???????????????????????????Hy?is?a?Lisp?dialect?that?translates?express…???29??????????????????[OK]????????????????
        arm64v8/python???????????????????Python?is?an?interpreted,?interactive,?objec…???24??????????????????????????????????????
        revolutionsystems/python?????????Optimized?Python?Images?????????????????????????18??????????????????????????????????????
        centos/python-27-centos7?????????Platform?for?building?and?running?Python?2.7…???17??????????????????????????????????????
        bitnami/python???????????????????Bitnami?Python?Docker?Image?????????????????????10??????????????????????????????????????[OK]
        publicisworldwide/python-conda???Basic?Python?environments?with?Conda.???????????6???????????????????????????????????????[OK]
        d3fk/python_in_bottle????????????Simple?python:alpine?completed?by?Bottle+Req…???5???????????????????????????????????????[OK]
        dockershelf/python???????????????Repository?for?docker?images?of?Python.?Test…???5???????????????????????????????????????[OK]
        clearlinux/python????????????????Python?programming?interpreted?language?with…???4???????????????????????????????????????
        i386/python??????????????????????Python?is?an?interpreted,?interactive,?objec…???3???????????????????????????????????????
        ppc64le/python???????????????????Python?is?an?interpreted,?interactive,?objec…???2???????????????????????????????????????
        centos/python-34-centos7?????????Platform?for?building?and?running?Python?3.4…???2???????????????????????????????????????
        amd64/python?????????????????????Python?is?an?interpreted,?interactive,?objec…???1???????????????????????????????????????
        ccitest/python???????????????????CircleCI?test?images?for?Python?????????????????0???????????????????????????????????????[OK]
        s390x/python?????????????????????Python?is?an?interpreted,?interactive,?objec…???0???????????????????????????????????????
        saagie/python????????????????????Repo?for?python?jobs????????????????????????????0

        最好選擇官方(OFFICIAL)的鏡像,這樣的鏡像最穩(wěn)定一些。

        3.3 下載新的鏡像

        執(zhí)行命令docker pull 鏡像名稱:版本號即可下載新的鏡像。

        $?docker?pull?python:3.8
        3.8:?Pulling?from?library/python
        6c33745f49b4:?Pull?complete?
        ef072fc32a84:?Pull?complete?
        c0afb8e68e0b:?Pull?complete?
        d599c07d28e6:?Pull?complete?
        f2ecc74db11a:?Pull?complete?
        26856d31ce86:?Pull?complete?
        2cd68d824f12:?Pull?complete?
        7ea1535f18c3:?Pull?complete?
        2bef93d9a76e:?Pull?complete?
        Digest:?sha256:9079aa8582543494225d2b3a28fce526d9a6b06eb06ce2bac3eeee592fcfc49e
        Status:?Downloaded?newer?image?for?python:3.8
        docker.io/library/python:3.8

        鏡像下載后,就可以使用鏡像來創(chuàng)建容器了。

        Docker容器的使用

        4.1 啟動容器

        執(zhí)行命令docker run即可啟動容器,也就是創(chuàng)建某個鏡像的實(shí)例。docker run命令非常復(fù)雜,可以先執(zhí)行一個docker run --help來查看幫助:

        $?docker?run?--help

        Usage:??docker?run?[OPTIONS]?IMAGE?[COMMAND]?[ARG...]

        Run?a?command?in?a?new?container

        Options:
        ??????--add-host?list??????????????????Add?a?custom?host-to-IP?mapping?(host:ip)
        ??-a,?--attach?list????????????????????Attach?to?STDIN,?STDOUT?or?STDERR
        ??????--blkio-weight?uint16????????????Block?IO?(relative?weight),?between?10?and?1000,?or?0?to?disable?(default?0)
        ??????--blkio-weight-device?list???????Block?IO?weight?(relative?device?weight)?(default?[])
        ??????--cap-add?list???????????????????Add?Linux?capabilities
        ??????--cap-drop?list??????????????????Drop?Linux?capabilities
        ??????--cgroup-parent?string???????????Optional?parent?cgroup?for?the?container
        ??????--cidfile?string?????????????????Write?the?container?ID?to?the?file
        ??????--cpu-period?int?????????????????Limit?CPU?CFS?(Completely?Fair?Scheduler)?period
        ??????--cpu-quota?int??????????????????Limit?CPU?CFS?(Completely?Fair?Scheduler)?quota
        ??????--cpu-rt-period?int??????????????Limit?CPU?real-time?period?in?microseconds
        ??????--cpu-rt-runtime?int?????????????Limit?CPU?real-time?runtime?in?microseconds
        ??-c,?--cpu-shares?int?????????????????CPU?shares?(relative?weight)
        ??????--cpus?decimal???????????????????Number?of?CPUs
        ??????--cpuset-cpus?string?????????????CPUs?in?which?to?allow?execution?(0-3,?0,1)
        ??????--cpuset-mems?string?????????????MEMs?in?which?to?allow?execution?(0-3,?0,1)
        ??-d,?--detach?????????????????????????Run?container?in?background?and?print?container?ID
        ??????--detach-keys?string?????????????Override?the?key?sequence?for?detaching?a?container
        ??????--device?list????????????????????Add?a?host?device?to?the?container
        ??????--device-cgroup-rule?list????????Add?a?rule?to?the?cgroup?allowed?devices?list
        ??????--device-read-bps?list???????????Limit?read?rate?(bytes?per?second)?from?a?device?(default?[])
        ??????--device-read-iops?list??????????Limit?read?rate?(IO?per?second)?from?a?device?(default?[])
        ??????--device-write-bps?list??????????Limit?write?rate?(bytes?per?second)?to?a?device?(default?[])
        ??????--device-write-iops?list?????????Limit?write?rate?(IO?per?second)?to?a?device?(default?[])
        ??????--disable-content-trust??????????Skip?image?verification?(default?true)
        ??????--dns?list???????????????????????Set?custom?DNS?servers
        ??????--dns-option?list????????????????Set?DNS?options
        ??????--dns-search?list????????????????Set?custom?DNS?search?domains
        ??????--domainname?string??????????????Container?NIS?domain?name
        ??????--entrypoint?string??????????????Overwrite?the?default?ENTRYPOINT?of?the?image
        ??-e,?--env?list???????????????????????Set?environment?variables
        ??????--env-file?list??????????????????Read?in?a?file?of?environment?variables
        ??????--expose?list????????????????????Expose?a?port?or?a?range?of?ports
        ??????--gpus?gpu-request???????????????GPU?devices?to?add?to?the?container?('all'?to?pass?all?GPUs)
        ??????--group-add?list?????????????????Add?additional?groups?to?join
        ??????--health-cmd?string??????????????Command?to?run?to?check?health
        ??????--health-interval?duration???????Time?between?running?the?check?(ms|s|m|h)?(default?0s)
        ??????--health-retries?int?????????????Consecutive?failures?needed?to?report?unhealthy
        ??????--health-start-period?duration???Start?period?for?the?container?to?initialize?before?starting?health-retries?countdown?(ms|s|m|h)?(default?0s)
        ??????--health-timeout?duration????????Maximum?time?to?allow?one?check?to?run?(ms|s|m|h)?(default?0s)
        ??????--help???????????????????????????Print?usage
        ??-h,?--hostname?string????????????????Container?host?name
        ??????--init???????????????????????????Run?an?init?inside?the?container?that?forwards?signals?and?reaps?processes
        ??-i,?--interactive????????????????????Keep?STDIN?open?even?if?not?attached
        ??????--ip?string??????????????????????IPv4?address?(e.g.,?172.30.100.104)
        ??????--ip6?string?????????????????????IPv6?address?(e.g.,?2001:db8::33)
        ??????--ipc?string?????????????????????IPC?mode?to?use
        ??????--isolation?string???????????????Container?isolation?technology
        ??????--kernel-memory?bytes????????????Kernel?memory?limit
        ??-l,?--label?list?????????????????????Set?meta?data?on?a?container
        ??????--label-file?list????????????????Read?in?a?line?delimited?file?of?labels
        ??????--link?list??????????????????????Add?link?to?another?container
        ??????--link-local-ip?list?????????????Container?IPv4/IPv6?link-local?addresses
        ??????--log-driver?string??????????????Logging?driver?for?the?container
        ??????--log-opt?list???????????????????Log?driver?options
        ??????--mac-address?string?????????????Container?MAC?address?(e.g.,?92:d0:c6:0a:29:33)
        ??-m,?--memory?bytes???????????????????Memory?limit
        ??????--memory-reservation?bytes???????Memory?soft?limit
        ??????--memory-swap?bytes??????????????Swap?limit?equal?to?memory?plus?swap:?'-1'?to?enable?unlimited?swap
        ??????--memory-swappiness?int??????????Tune?container?memory?swappiness?(0?to?100)?(default?-1)
        ??????--mount?mount????????????????????Attach?a?filesystem?mount?to?the?container
        ??????--name?string????????????????????Assign?a?name?to?the?container
        ??????--network?network????????????????Connect?a?container?to?a?network
        ??????--network-alias?list?????????????Add?network-scoped?alias?for?the?container
        ??????--no-healthcheck?????????????????Disable?any?container-specified?HEALTHCHECK
        ??????--oom-kill-disable???????????????Disable?OOM?Killer
        ??????--oom-score-adj?int??????????????Tune?host's?OOM?preferences?(-1000?to?1000)
        ??????--pid?string?????????????????????PID?namespace?to?use
        ??????--pids-limit?int?????????????????Tune?container?pids?limit?(set?-1?for?unlimited)
        ??????--platform?string????????????????Set?platform?if?server?is?multi-platform?capable
        ??????--privileged?????????????????????Give?extended?privileges?to?this?container
        ??-p,?--publish?list???????????????????Publish?a?container's?port(s)?to?the?host
        ??-P,?--publish-all????????????????????Publish?all?exposed?ports?to?random?ports
        ??????--read-only??????????????????????Mount?the?container's?root?filesystem?as?read?only
        ??????--restart?string?????????????????Restart?policy?to?apply?when?a?container?exits?(default?"no")
        ??????--rm?????????????????????????????Automatically?remove?the?container?when?it?exits
        ??????--runtime?string?????????????????Runtime?to?use?for?this?container
        ??????--security-opt?list??????????????Security?Options
        ??????--shm-size?bytes?????????????????Size?of?/dev/shm
        ??????--sig-proxy??????????????????????Proxy?received?signals?to?the?process?(default?true)
        ??????--stop-signal?string?????????????Signal?to?stop?a?container?(default?"SIGTERM")
        ??????--stop-timeout?int???????????????Timeout?(in?seconds)?to?stop?a?container
        ??????--storage-opt?list???????????????Storage?driver?options?for?the?container
        ??????--sysctl?map?????????????????????Sysctl?options?(default?map[])
        ??????--tmpfs?list?????????????????????Mount?a?tmpfs?directory
        ??-t,?--tty????????????????????????????Allocate?a?pseudo-TTY
        ??????--ulimit?ulimit??????????????????Ulimit?options?(default?[])
        ??-u,?--user?string????????????????????Username?or?UID?(format:?[:<group|gid>])
        ??????--userns?string??????????????????User?namespace?to?use
        ??????--uts?string?????????????????????UTS?namespace?to?use
        ??-v,?--volume?list????????????????????Bind?mount?a?volume
        ??????--volume-driver?string???????????Optional?volume?driver?for?the?container
        ??????--volumes-from?list??????????????Mount?volumes?from?the?specified?container(s)
        ??-w,?--workdir?string?????????????????Working?directory?inside?the?container

        比如我們要執(zhí)行python的shell,需要添加-it參數(shù),即:docker run -it python:3.8

        $?docker?run?-it?python:3.8?
        Python?3.8.7?(default,?Dec?22?2020,?18:46:25)?
        [GCC?8.3.0]?on?linux
        Type?"help",?"copyright",?"credits"?or?"license"?for?more?information.
        >>>?

        4.2 將宿主機(jī)的文件掛載到容器

        docker容器與宿主機(jī)是隔離的,要想讓容器內(nèi)的程序能訪問宿主機(jī)上的文件,需要通過-v參數(shù)將宿主機(jī)的文件掛載到容器中。

        比如我們在宿主機(jī)上有一個hello.py,可以打印hello,想要在python容器中執(zhí)行,就需要進(jìn)行掛載。-v后還需要接兩個參數(shù),分別是宿主機(jī)的目錄和容器內(nèi)的目錄,兩者使用:分隔,路徑必須都是絕對路徑。

        我的hello.py保存在主目錄的/docker_test目錄中,將這個目錄掛載到容器的/docker_test目錄,然后在容器內(nèi)執(zhí)行python /docker_test/hello.py:

        $?docker?run?-v?~/docker_test:/docker_test?python:3.8?python?/docker_test/hello.py
        hello

        4.3 容器的端口映射

        我們修改一下hello.py,創(chuàng)建一個socket服務(wù)端,并監(jiān)聽5000端口,當(dāng)有客戶端連接時,打印客戶端的地址,先客戶端發(fā)送hello,然后關(guān)閉連接:

        import?socket

        ip_port?=?('127.0.0.1',?5000)

        sk?=?socket.socket()
        sk.bind(ip_port)
        sk.listen(5)

        while?True:
        ????print('server?waiting...')
        ????conn,addr?=?sk.accept()
        ????print(addr)
        ????conn.sendall(b'hello\n')
        ????conn.close()

        在容器內(nèi)執(zhí)行:

        docker?run?-v?~/docker_test:/docker_test?python:3.8?python?/docker_test/hello.py

        接下來,嘗試用telnet命令連接,結(jié)果卻是失敗的。原因是,127.0.0.1是宿主機(jī)的ip地址,5000是容器的端口,這與我們的習(xí)慣稍微有些不同。事實(shí)上,docker的容器是非常輕量的,它并沒有自己的網(wǎng)絡(luò),要想訪問容器的端口,需要進(jìn)行端口映射,將容器的某端口映射到宿主機(jī)的端口,客戶端連接時,只要與宿主機(jī)的端口進(jìn)行連接就可以了。

        需要注意的是,上面的代碼創(chuàng)建的服務(wù)器,無論如何也不可能被客戶端連接,因?yàn)榇a中綁定了127.0.0.1的ip,在容器中運(yùn)行時,需要綁定所有ip,即0.0.0.0。

        import?socket

        ip_port?=?('0.0.0.0',?5000)

        sk?=?socket.socket()
        sk.bind(ip_port)
        sk.listen(5)

        while?True:
        ????print('server?waiting...')
        ????conn,addr?=?sk.accept()
        ????print(addr)
        ????conn.sendall(b'hello\n')
        ????conn.close()

        然后,再使用-p參數(shù),-p還需要三個參數(shù),即宿主機(jī)的ip地址、宿主機(jī)的端口、容器的端口,三者之間使用:分隔。一般的,可以將宿主機(jī)的ip地址省略,只寫宿主機(jī)的端口:容器的端口即可。

        docker?run?-v?~/docker_test:/docker_test?-it?-p?5001:5000?python:3.8?python?/docker_test/hello.py

        這樣,就將容器的5000端口映射到了宿主機(jī)的5001端口,使用:

        telnet?127.0.0.1?5001

        即可與容器中的服務(wù)器進(jìn)行連接。

        4.4 容器管理

        上面的服務(wù)運(yùn)行之后,可以使用docker ps命令,查看運(yùn)行中的容器:

        $?docker?ps
        CONTAINER?ID?????IMAGE???????????COMMAND??????????????????CREATED???????????STATUS?????????PORTS????????????????????NAMES
        ec4c86b8a163?????python:3.8??????"python?/docker_test…"???5?seconds?ago?????Up?4?seconds???0.0.0.0:5000->5000/tcp???eager_wilson

        顯示的內(nèi)容有下面幾列:

        • CONTAINER ID:容器ID

        • IMAGE:鏡像名稱和版本

        • COMMAND:執(zhí)行的命令

        • CREATED:容器創(chuàng)建時間

        • STATUS:容器的狀態(tài)

        • PORTS:端口映射

        • NAMES:容器名

        要想結(jié)束容器,可以使用docker kill 容器ID命令。

        自制Docker鏡像

        一般而言,當(dāng)我們的程序開發(fā)完成后,會連同程序文件與運(yùn)行環(huán)境一起制作成一個新的鏡像。

        要制作鏡像,需要編寫Dockerfile。DockeFile由多個命令組成,常用的命令有:

        • FROM:基于某個鏡像來制作新的鏡像。格式為:FROM 鏡像名稱:鏡像版本。

        • COPY:從宿主機(jī)復(fù)制文件,支持?、*等通配符。格式為:COPY 源文件路徑 目標(biāo)文件路徑。

        • ADD:從宿主機(jī)添加文件,格式與COPY相同,區(qū)別在于當(dāng)文件為壓縮文件時,會解壓縮到目標(biāo)路徑。

        • RUN:在創(chuàng)建新鏡像的過程中執(zhí)行的shell命令。格式為:RUN shell命令行。注意,此shell命令將在容器內(nèi)執(zhí)行。

        • CMD:在容器實(shí)例中運(yùn)行的命令,格式與RUN相同。注意,如果在docker run時指定了命令,將不會執(zhí)行CMD的內(nèi)容。

        • ENTRYPOINT:在容器實(shí)例中運(yùn)行的命令,格式與CMD相同。注意,如果在docker run時指定了命令,該命令會以命令行參數(shù)的形式傳遞到ENTRYPOINT中。

        • ENV:在容器中創(chuàng)建環(huán)境變量,格式為:ENV 變量名值。

        注意,Docker鏡像中有一個層的概念,每執(zhí)行一個RUN命令,就會創(chuàng)建一個層,層過多會導(dǎo)致鏡像文件體積增大。盡量在RUN命令中使用&&連接多條shell命令,減少RUN命令的個數(shù),可以有效減小鏡像文件的體積。

        5.1 自制顯示文本文件內(nèi)容鏡像

        編寫cat.py,接收一個文件名,由python讀取文件并顯示文件的內(nèi)容:

        import?os
        import?sys

        input?=?sys.argv[1]

        with?open(input,?"r")?as?fp:
        ????print(fp.read())

        這個例子比較簡單,縮寫Dockerfile如下:

        FROM?python:3.8
        WORKDIR?/files
        COPY?cat.py?/cat.py
        ENTRYPOINT?["python",?"/cat.py"]

        這個Dockerfile的含義是:

        • 以python:3.8為基礎(chǔ)鏡像

        • 容器啟動命令的工作目錄為/files,在運(yùn)行鏡像時,需要我們把宿主機(jī)的某目錄掛載到容器的/files目錄

        • 復(fù)制cat.py到容器的根目錄下

        • 啟動時運(yùn)行python /cat.py命令

        需要說明的是,ENTRYPOINT有兩種寫法:

        ENTRYPOINT?python?/cat.py
        ENTRYPOINT?["python",?"/cat.py"]

        這里采用第二種寫法,是因?yàn)槲覀円谕獠拷o容器傳遞參數(shù)。執(zhí)行命令編譯Docker鏡像:

        docker?build?-t?cat:1.0?.

        這個命令中,-t的含義是目標(biāo),即生成的鏡像名為hello,版本號為1.0,別忘了最后那個.,這叫到上下文路徑,是指 docker 在構(gòu)建鏡像,有時候想要使用到本機(jī)的文件(比如復(fù)制),docker build 命令得知這個路徑后,會將路徑下的所有內(nèi)容打包。

        這樣,我們的第一個鏡像就制作完成了,使用下面的命令執(zhí)行它:

        docker?run?-it?-v?~/docker_test/cat/files:/files?cat:1.0?test.txt

        即可看到~/docker_test/cat/files/test.txt的內(nèi)容。

        5.2 自制web服務(wù)器鏡像

        我們使用tornado開發(fā)一個網(wǎng)站,而python的官方鏡像是沒有tornado庫的,這就需要在制作鏡像時進(jìn)行安裝。

        測試的ws.py如下:

        import?tornado.httpserver
        import?tornado.ioloop
        import?tornado.options
        import?tornado.web

        from?tornado.options?import?define,?options
        define("port",?default=8000,?help="run?on?the?given?port",?type=int)

        class?IndexHandler(tornado.web.RequestHandler):
        ????def?get(self):
        ????????self.write("Hello?world")

        if?__name__?==?"__main__":
        ????tornado.options.parse_command_line()
        ????app?=?tornado.web.Application(handlers=[(r"/",?IndexHandler)])
        ????http_server?=?tornado.httpserver.HTTPServer(app)
        ????http_server.listen(options.port)
        ????tornado.ioloop.IOLoop.instance().start()

        編寫Dockerfile文件如下:

        FROM?python:3.8
        WORKDIR?/ws
        COPY?ws.py?/ws/ws.py
        RUN?pip?install?tornado
        CMD?python?hello.py

        在此我們驗(yàn)證一下CMD與ENTRYPOINT的區(qū)別。在Dockerfile所在有目錄下執(zhí)行如下命令:

        docker?build?-t?ws:1.0?.

        執(zhí)行完成后,再使用docker images使用就可以看到生成的鏡像了,然后使用下面的命令運(yùn)行:

        docker?run?-it?-p?8000:8000?ws:1.0

        在瀏覽器中輸入宿主機(jī)的ip和8000端口,就可以看到頁面了。

        在這個例子中,我使用的運(yùn)行命令是CMD,如果在docker run中指定的其他的命令,此命令就不會被執(zhí)行,如:

        $?docker?run?-it?-p?8000:8000?ws:1.0?python
        Python?3.8.7?(default,?Dec?22?2020,?18:46:25)?
        [GCC?8.3.0]?on?linux
        Type?"help",?"copyright",?"credits"?or?"license"?for?more?information.
        >>>?

        此時,容器中被執(zhí)行的是python命令,而不是我們的服務(wù)。在更多情況下,我們希望在docker run命令中為我們的服務(wù)傳參,而不是覆蓋執(zhí)行命令,那么,我們應(yīng)該使用ENTRYPOINT而不是CMD:

        FROM?python:3.8
        WORKDIR?/ws
        COPY?ws.py?/ws/ws.py
        RUN?pip?install?tornado
        ENTRYPOINT?python?ws.py

        上面這種寫法,是不支持傳遞參數(shù)的,ENTRYPOINT和CMD還支持另一種寫法:

        FROM?python:3.8
        WORKDIR?/ws
        COPY?ws.py?/ws/ws.py
        RUN?pip?install?tornado
        ENTRYPOINT?["python",?"ws.py"]

        使用這種寫法,docker run命令中的參數(shù)才可以傳遞給hello.py:

        docker?run?-it?-p?8000:9000?ws:1.0?--port=9000

        這個命令中,--port=9000被作為參數(shù)傳遞到hello.py中,因此容器內(nèi)的端口就成了9000。

        在生產(chǎn)環(huán)境中運(yùn)行時,不會使用-it選項(xiàng),而是使用-d選項(xiàng),讓容器在后臺運(yùn)行:

        $?docker?run?-d?-p?8000:9000?ws:1.0?--port=9000
        4a2df9b252e2aff6a8853b3a8bf46c0577545764831bb7557b836ddcd85cba70
        $?docker?ps???????????????????????????????????????
        CONTAINER?ID???IMAGE????????COMMAND??????????????????CREATED???????????STATUS????????????PORTS????????????????????NAMES
        4a2df9b252e2???hello:1.0????"python?ws.py?--p…"???9?seconds?ago?????Up?8?seconds??????0.0.0.0:8000->9000/tcp???elegant_sammet

        這種方式下,即使當(dāng)前的控制臺被關(guān)閉,該容器也不會停止。

        5.3 自制apscheduler服務(wù)鏡像

        接下來,制作一個使用apscheduler編寫的服務(wù)鏡像,代碼如下:

        import?sys
        import?shutil
        from?apscheduler.schedulers.blocking?import?BlockingScheduler
        from?apscheduler.triggers.cron?import?CronTrigger

        def?scan_files():
        ????shutil.copytree(sys[1],?sys[2])

        scheduler?=?BlockingScheduler()
        scheduler.add_job(
        ????scan_files,
        ????trigger=CronTrigger(minute="*"),
        ????misfire_grace_time=30
        )

        Dockerfile也是信手拈來:

        FROM?python:3.8
        WORKDIR?/
        COPY?sch.py?/sch.py
        RUN?pip?install?apscheduler
        ENTRYPOINT?["python",?"sch.py"]

        生成鏡像:

        docker?build?-t?sch:1.0?.

        應(yīng)該可以運(yùn)行了,文件復(fù)制需要兩個目錄,在運(yùn)行時,可以使用兩次-v來掛載不同的目錄:

        docker?run?-d?-v?~/docker_test/sch/src:/src?-v?~/docker_test/sch/dest:/dest?sch:1.0?/src?/dest

        多階段構(gòu)建壓縮鏡像體積

        前面用到的官方python鏡像大小足足882MB,在這個基礎(chǔ)上,再安裝用到的第三方庫,添加項(xiàng)目需要的圖片等資源,大小很容易就超過1個G,這么大的鏡像,網(wǎng)絡(luò)傳給客戶非常的不方便,因此,減小鏡像的體積是非常必要的工作。

        docker hub上有個一python:3.8-alpine鏡像,大小只有44.5MB。之所以小,是因?yàn)閍lpine是一個采用了busybox架構(gòu)的操作系統(tǒng),一般用于嵌入式應(yīng)用。我嘗試使用這個鏡像,發(fā)現(xiàn)安裝一般的庫還好,但如果想安裝numpy等就會困難重重,甚至網(wǎng)上都找不到解決方案。

        還是很回到基本的路線上來,主流的操作系統(tǒng)鏡像,ubuntu的大小為72.9MB,centos的大小為209MB——這也算是我更喜歡使用ubuntu的一個重要原因吧!使用ubuntu作為基礎(chǔ)鏡像,安裝python后的大小為139MB,再安裝pip后的大小一下子上升到了407MB,要是再安裝點(diǎn)其他東西,很容易就趕上或超過python官方鏡像的大小了。

        看來,尋常路線是很難壓縮鏡像文件體積了。幸好,還有一條曲線救國的路可走,這就是多階段構(gòu)建法。

        多階段構(gòu)建的思想其實(shí)很簡單,先構(gòu)建一個大而全的鏡像,然后只把鏡像中有用的部分拿出來,放在一個新的鏡像里。在我們的場景下,pip只在構(gòu)建鏡像的過程中需要,而對運(yùn)行我們的程序卻一點(diǎn)用處也沒有。我們只需要安裝pip,再用pip安裝第三方庫,然后將第三方庫從這個鏡像中復(fù)制到一個只有python,沒有pip的鏡像中,這樣,pip占用的268MB空間就可以被節(jié)省出來了。

        1、在ubuntu鏡像的基礎(chǔ)上安裝python:

        FROM?ubuntu
        RUN?apt?update?\
        ????&&?apt?install?python3

        然后運(yùn)行:

        docker?build?-t?python:3.8-ubuntu?.

        這樣,就生成了python:3.8-ubuntu鏡像。

        2、在python:3.8-ubuntu的基礎(chǔ)上安裝pip:

        FROM?python:3.8-ubuntu
        RUN?apt?install?python3

        然后運(yùn)行:

        docker?build?-t?python:3.8-ubuntu-pip?.

        這樣,就生成了python:3.8-ubuntu-pip鏡像。

        3、多階段構(gòu)建目標(biāo)鏡像:

        FROM?python:3.8-ubuntu-pip
        RUN?pip3?install?-i?https://pypi.tuna.tsinghua.edu.cn/simple?numpy
        FROM?python:3.8-ubuntu
        COPY?--from=0?/usr/local/lib/python3.8/dist-packages/?/usr/local/lib/python3.8/dist-packages/

        這個dockerfile需要解釋一下了,因?yàn)樗袃蓚€FROM命令。

        第一個是以python:3.8-ubuntu-pip鏡像為基礎(chǔ),安裝numpy,當(dāng)然,在實(shí)際應(yīng)用中,把所有用到的第三方庫出寫在這里。

        第二個FROM是以FROM python:3.8-ubuntu鏡像為基礎(chǔ),將第三方庫統(tǒng)統(tǒng)復(fù)制過來,COPY命令后的–from=0的意思是從第0階段進(jìn)行復(fù)制。實(shí)際應(yīng)用中再從上下文中復(fù)制程序代碼,添加需要的ENTRYPOINT等。

        最后,再運(yùn)行:

        docker?build?-t?project:1.0?.

        這然,用于我們項(xiàng)目的鏡像就做好了。比使用官方python鏡像構(gòu)建的版本,小了大約750MB。

        導(dǎo)入鏡像到生產(chǎn)環(huán)境

        到此,我們的鏡像已經(jīng)制作好了,可是,鏡像文件在哪,如何在生產(chǎn)環(huán)境下運(yùn)行呢?

        剛才使用docker images命令時,已經(jīng)看到了生成的鏡像:

        $?docker?images??????????????????????????
        REPOSITORY??????????TAG?????????????????IMAGE?ID????????????CREATED?????????????SIZE
        hello???????????????1.0?????????????????01fe19111dc7????????59?minutes?ago??????893MB
        python??????????????3.8?????????????????f5041c8ae6b1????????13?days?ago?????????884MB
        ubuntu??????????????20.04???????????????f643c72bc252????????5?weeks?ago?????????72.9MB
        hello-world?????????latest??????????????bf756fb1ae65????????12?months?ago???????13.3kB

        我們可以使用docker save命令將鏡像保存到指定的文件中,保存的文件是一個.tar格式的壓縮文件:

        docker?save?-o?hello.tar?hello:1.0

        將hello.tar復(fù)制到生產(chǎn)環(huán)境的機(jī)器上,然后執(zhí)行導(dǎo)入命令:

        docker?load?-i?hello.tar

        就可以使用了。

        作者?|?天元浪子

        來源?|?CSDN博客

        往期精彩回顧




        瀏覽 46
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            大学生被乞丐肉h | 免费在线观看黄色视频网站 | 男的操女的逼 | 天天干天 | www性欧美 | 天天操天天操天天 | 成人小说av | 717yy欧美电影夜夜福利 | 乡村淫交小说 | 日比视频免费 |