<dl id="3wz6h"></dl><li id="3wz6h"></li>

      1. <dl id="3wz6h"></dl>

      2. <dl id="3wz6h"><ins id="3wz6h"></ins></dl>

            <dl id="3wz6h"></dl>

            <dl id="3wz6h"><ins id="3wz6h"></ins></dl>
            1. 
              
              <output id="3wz6h"><ins id="3wz6h"><nobr id="3wz6h"></nobr></ins></output>

              <li id="3wz6h"><ins id="3wz6h"></ins></li>
              
              

            2. <output id="3wz6h"><ins id="3wz6h"><nobr id="3wz6h"></nobr></ins></output>
              首頁»WEB綜合»Docker 入門與實踐

              Docker 入門與實踐

              來源:mrcode 發布時間:2018-04-18 閱讀次數:
              Docker 是一個能讓程序跑在一個它無法感知的、用于隔絕外界環境里的容器的工具。

               Docker 簡介

                最初是 dotCloud 公司創始人 Solomon Hykes 發起的一個公司內部項目,并于 2013 年 3 月以 Apache 2.0 授權協議開源,代碼主要在 GitHub 上進行維護。Docker 項目后來還加入了 Linux 基金會,并成立推動 開放容器聯盟(OCI)

                Docker 使用 Google 推出的 Go 語言開發實現,基于 Linux 內核的 cgroupnamespace,以及 UnionFS 等技術。最初實現基于 LXC,從 0.7 版本后去除 LXC,轉而開始使用自行開發的 libcontainer,從 1.11 開始,則進一步演進為使用 runCcontainerd

                在 2017 年 4 月 21 日 Pull Request #32691 將原有的 Docker 項目更名為 Moby,由 Moby 構建出 Docker CE(社區版),而新的 Docker 項目則構建出 Docker EE(企業版本)。

               Docker 初步了解

                Docker 容器與虛擬機的區別

                原理不同

                上圖是 Docker Doc 關于 Docker 和傳統虛擬機區別的截圖。

                Docker 利用了 Linux 內核的 cgroup 和 namespace 為程序的執行創造一個隔離的環境,使得程序感知不到外界的存在,其本身仍然是跑在原有的內核上的;而虛擬機則是通過 Hypervisor 模擬了一整套系統環境,虛擬機里的程序是跑在虛擬機內核上的。由于虛擬機需要模擬一整套操作系統環境,因此開銷比 Docker 容器要高很多很多。

                你可以把跑在容器里的程序想象成楚門(楚門的世界男主),他并不知道自己生活在一個精心布置的超大影棚里,但是他仍然是活在現實世界里的,呼吸著現實世界中的空氣,吃著和我們差不多的食物;跑在虛擬機里的程序就好像活在動畫片里的小豬佩奇,他的一切都是虛擬的,雖然小豬佩奇并不知道自己活在動畫片里,但是很顯然的是它和我們完全不在一個世界(不是同一個系統內核)。

                性能差異

              特性 容器 虛擬機
              啟動 秒級 分鐘級
              硬盤使用 一般為 MB 一般為 GB
              性能 接近原生 弱于
              系統支持量 單機上千個容器 一般幾十個

                Docker 基本概念

                鏡像

              鏡像 是一個包含操作系統完整 root 文件系統 的、只讀的,由多層文件系統聯合而成的打包文件。

                Docker 為了讓應用無感知的跑在容器中,提供了一套完整的 root 文件系統,比如官方鏡像 library/ubuntu 就包含了一整套 root 文件系統。像 apache、nginx 都是基于該鏡像構建的,由于 library/ubuntu 本身很大,所以 Docker 采用了分層存儲的方式。

                本文假裝你已經安裝了 Docker,上圖通過 docker pull nginx 從 官方 Registry(下面會提到這是啥)拉取 nginx 鏡像,拉取 nginx 相當于 library/nginx:latest,library 表示 nginx 是官方鏡像,因此可以省略,:latest 表示拉取標簽為 latest 的鏡像。拉取后可以看到存在兩個鏡像,因為 nginx 鏡像本身就是基于 library:ubuntu:16.04 鏡像的。

                上圖通過 docker pull httpd 拉取了 apache 鏡像,由于 ubuntu:16.04 鏡像已經在本地存在了,因此拉取的時候不會重復拉取。從而節約拉取時間。這就是 Docker 分層存儲的意義。

                鏡像的只讀可以理解成以前的光盤 CD,是不可更改的。為了模擬實現對光盤 CD 的寫的功能,會建立兩層文件系統,一層是光盤 CD 的只讀文件系統;另外一層是存放更改數據的可寫的文件系統。從而實現模擬更改鏡像的作用。Docker 也是采用這種類似的分層的方式。

                如圖,可以看出 ubuntu:15.04 是由很多層文件系統(鏡像)堆疊形成的,最底層是 root 文件系統(d3a1f33e8a5a)。這幾層文件系統都被設置成只讀的。多層文件系統利用了上面提到的 UnionFS、AUFS、OverlayFS,這是一類文件系統,這種聯合掛載文件系統最早就是用于解決 CD 這種只讀文件系統的修改問題,Docker 之前使用 AUFS,但是由于 AUFS 不被 linus 喜歡(被 linus 評價為稠密、不可讀,無注釋)導致 AUFS 一直沒有被合并到 Linux 的主分支中。Docker 在 1.12 以后已經將默認的文件系統從 AUFS 替換成 OverlayFS2。因為 OverlayFS2 已經被合并進了 Linux 的主干分支中。

                容器

                上面我們拉取了 nginx 鏡像到本地,我們可以使用 docker container start nginx(省略了 latest 標簽)來運行這個鏡像。運行之前會先創建一個容器(其實本質就是創建了一層可讀寫的文件系統,以提供程序運行時的讀寫支持),然后就會啟動程序,讓程序跑在一個隔離環境(不是虛擬環境)里。你還可以通過 docker container commit>來對當前層進行提交(就好像 Git 提交一樣),從而形成一個新的鏡像,但是這種方式是不推薦的;這是因為在程序運行過程中可能會產生一些垃圾文件,而如果這些垃圾文件被提交后,新的鏡像又是不可修改的,只會增大鏡像的體積。具體怎么創建鏡像會在下面說到。

                可以看到上圖中在創建容器的時候其實就是創建了一個容器可讀寫層。你還可以通過 docker container stop<container ID> 停止容器的運行,相當于 kill 掉容器內的正在運行的程序,但是創建容器時創建的可讀寫的文件系統依然存在。所以你依然可以通過 docker start <container ID> 來重啟程序。

                倉庫

                鏡像構建完成后,可以很容易的在宿主機器上運行,但是如果其他機器要使用這個鏡像,我們就需要一個集中存儲、分發鏡像的服務,Docker Registry 就是這樣的服務。一個 Docker Registry 可以包含多個倉庫,每個倉庫可以包含多個標簽,每個標簽對應一個鏡像。

                就拿上面的 library/nginx:latest 舉例,library 表示這個鏡像是官方鏡像,如果不是官方鏡像,這里一般填注冊在 Docker Registry 的用戶名;library/nginx 是倉庫的名字,latest 是該倉庫一個標簽。

                誠然,官方的 Docker Registry 是世界上最大的鏡像分發服務,官方還提供了 Docker Registry 鏡像 用于搭建私有鏡像分發服務。而且 DockerHub 和社區一起制作了大量的、高質量的鏡像,使得我們構建鏡像更為方便。

               Docker 簡單實踐

                Docker 單個鏡像

                前面提到可以通過 docker commit 生成新的鏡像,但是這種方式并不推薦(原因已經說明),所以我們一般還是采用 Dockerfile 的方式。下面的實踐以 github-issue-rss 為例,demonstate how to containerization a normal project。

                首先創建一個 Dockerfile 文件,內容如下:

              FROM node:9-alpine
              
              MAINTAINER mrcode "[email protected]"
              
              WORKDIR /src # 表示容器內的程序運行時的當前目錄
              
              COPY . /src # 把構建 Dockerfile 文件目錄下的文件全部復制到鏡像的 /src 目錄下
              RUN npm install -g yarn && yarn install # 構建時執行
              
              EXPOSE 3000 # 暴露容器的 3000 端口到外面
              
              ENTRYPOINT ["npm", "start"] # 執行 docker start <container ID> 時就會執行 npm start

                Dockerfile 里的每一行開頭的大寫字母單詞叫做 Dockerfile 指令。每執行一條指令就會增加一層鏡像(本質是執行了一次 docker commit,而 AUFS 最大的層數是 127 層,因此 Dockerfile 里的層數最好不要太多!

                FROM 表示基于哪一個鏡像構建,node:9-alpine 表示基于官方的 node 鏡像構建,標簽 9-alpine 表示這是一個 node 9 的鏡像,同時該 node9 鏡像是基于 alpine 鏡像構建的,alpine 是 Linux 的一個精簡發行版,大小只有 5MB 左右,而 Ubuntu 鏡像大小接近 200MB。

                RUN 指令會在構建鏡像時執行,使用 && 符號是為了減少 RUN 命令的使用次數,減少最終鏡像的層數。

                EXPOSE 指令讓外界能通過容器的 3000 端口進行網絡通信。

                ENTRYPOINT 表示執行 docker start <container ID> 時就會執行 npm start(啟動程序);還可以寫成 ENTRYPOINT npm start 這種形式;然后就可以開始構建了。有的同學喜歡在 npm start 后加上 '&',來讓容器默認后臺運行;但這只會導致容器無法啟動,因為容器本身的執行完全是依靠程序本身的進程的,當程序本身進程沒有掛載在 docker 容器上時,容器就會直接結束,容器結束后容器內的進程也被殺掉。所以要知道保持容器運行的正是容器內的進程本身!

                圖中執行命令最后有一個 '.',這是將當前目錄作為上下文傳遞給 Docker daemon;Docker 的工作方式是基于 C-S 架構的,你需要將構建的所在目錄傳給 docker daemon,這也是上面的 Dockerfile 文件的 COPY 指令的當前目錄。

                接下來創建容器,一個鏡像可以創建多個容器(其實就是創建多個在同一層的讀寫層)。

                docker run 會拉取遠程的鏡像(如果本地沒有的話),接著它會創建一個容器,基于 mrcode/github-issue-rss:test 鏡像(只有 latest 標簽可以省略);-v 會創建一個數據卷(volume),表示當容器對 /var/log/github-issue-rss/ 寫入數據時相當于寫在了宿主機的 ~/github-issue-rss/log 目錄上,從而維持容器的無狀態特性(無狀態特性是指容器在運行時盡量不要將重要數據存儲在容器所在的讀寫層里,雖然那是一層讀寫層,但是是用來存放程序運行時產生的臨時文件的,不應將重要數據放在里面);-d 表示 daemon 執行程序,否則的話容器進程會掛載在當前 shell 上,一般通過 -d 掛載到 docker daemon 進程上;—rm 表示容器退出后自動刪除容器,這是推薦的用法,也是容器的無狀態特性的體現。

              容器進程具有和容器內程序本身進程相同的生命周期,容器進程用來啟動容器內程序,相當于 Linux 內的 init 進程;當容器內程序被 docker stop <container ID> 殺掉時,容器就會退出,留下一個已創建的讀寫層文件系統,這也是容器存在的標志。

                由于創建容器僅僅是創建了一個可讀寫的文件系統,所以容器的存在是非常非常輕量級的。即便對一個鏡像創建多個容器,鏡像本身是不會被重新拷貝的,而是最大程度的復用,這是因為鏡像內的多層文件系統的每一層都被設置成只讀的。

                你可以通過 docker container ls 查看當前正在運行的所有容器,如果還想查看已退出的容器,加上一個 -a 參數。使用 docker container start/stop 可以啟動/關閉容器。

                最后可以通過 docker push mrcode/github-issue-rss:test 發布到 DockerHub 上,分享到社區。

                Docker 多個鏡像

                github-issue-rssis a tool converts the issues on GitHub to RSS.

                這個工具需要用到了 mysql,為了以后方便數據遷移,我決定使用 mysql 鏡像,mysql 鏡像可以把所有狀態存放在宿主機的一個文件夾下。那我現在不僅需要啟動 mysql 和 github-issue-rss 鏡像,還需要建立他們之間的網絡連接關系,事情變得麻煩了。有一個工具叫 docker-compose (本文假裝你已經安裝了這個工具)可以把這一切自動化。下面是項目根目錄的一個 docker-compose.yml 文件:

              version: "3"
              services:
                  db:
                      image: mysql:5.7
                      volumes:
                          - ~/.github-issue-rss/mysql:/var/lib/mysql
                      restart: always
                      environment:
                          MYSQL_ROOT_PASSWORD: rootroot
                          MYSQL_DATABASE: rss
                          MYSQL_USER: mrcode
                          MYSQL_PASSWORD: github-issue-rss
                  github-issue-rss:
                      image: mrcode/github-issue-rss:v0.1.0
                      depends_on:
                          - db
                      ports:
                          - "3000:3000"
                      restart: always
                      environment:
                          MYSQL_PORT: 3306
                          MYSQL_HOST: db
                          MYSQL_SCHEMA: rss
                          MYSQL_USERNAME: mrcode
                          MYSQL_PASSWORD: github-issue-rss
                          LOG_FILE: /var/log/github-issue-rss/
                      volumes:
                          - ~/.github-issue-rss/log/:/var/log/github-issue-rss/

                在 docker-compose 的世界里沒有容器,只有服務。它認為它啟動了兩服務 db 和 github-issue-rss。沒有哪個是主服務,所有服務都是平等的。

                在 db service 中,設置了 volumes,將 mysql 的數據存儲在 ~/.github-issue-rss/mysql/ 里,還可以設置更多的 volume。restart 表示只要服務執行失敗就重啟,防止依賴的 service 還沒有啟動完成時導致的錯誤引發連鎖反應。給兩個 service 配置的 environment 來建立兩者的數據連接,github-issue-rss 代碼會讀取這個環境變量,然后連接到 db 服務,可以看到 github-issue-rss 里的環境變量 MYSQL_HOST 設置為 db,這是因為 docker-compose 會在啟動的服務配置里建立這個 DNS 映射關系。

                還可以通過 docker-compose down 來停止并且刪除服務對應的容器。

                現在你只需要克隆倉庫到本地,然后執行 docker-compose up 就可以啟動 github-issue-rss 了,因為 github-issue-rss 鏡像本身已經構建并發布到 Docker Hub 了。

               Docker 的應用

                持續集成和持續交付

                使用 Docker 可以通過定制應用鏡像來實現持續集成、持續交付、部署。開發人員通過 Dockerfile 進行鏡像構建,結合持續集成系統進行集成測試,而運維人員則可以在生產環境中快速部署該鏡像。甚至結合持續部署進行自動部署。

                而且使用 Dockerfile 使鏡像的構建透明化,不僅可以幫助開發人員理解應用運行環境,也方便運維團隊理解應用運行所需條件,幫助更好的生產環境中部署該鏡像。

                微服務

                Docker 和微服務架構簡直就是渾然天成,站在 Docker 的角度,軟件本質是容器的組合:業務邏輯容器、數據庫容器、存儲容器、隊列容器……Docker 使得軟件拆分成若干的標準化容器,然后像積木一樣的搭建起來。這正是微服務的思想:軟件把任務外包出去,讓各種外部服務完成這些任務,軟件本身只是底層服務的調用中心和組裝層。

              QQ群:WEB開發者官方群(515171538),驗證消息:10000
              微信群:加小編微信 849023636 邀請您加入,驗證消息:10000
              提示:更多精彩內容關注微信公眾號:全棧開發者中心(fsder-com)
              網友評論(共0條評論) 正在載入評論......
              理智評論文明上網,拒絕惡意謾罵 發表評論 / 共0條評論
              登錄會員中心
              云南十一选往期