Docker入門——基礎概念,安裝運行Tomcat,MySQL

前言

這篇博客,基於之前一段時間對於Docker的簡單使用,南國在這裏對於Docker的基礎知識做一個簡要的總結。

虛擬機

首先我們簡要描述一下虛擬機,比如我們常用的VMWare、VirtualBox等。它可以在一種操作系統裏面運行另一種操作系統,比如在 Windows 系統裏面運行 Linux 系統。應用程序對此毫無感知,因爲虛擬機看上去跟真實系統一模一樣,而對於底層系統來說,虛擬機就是一個普通文件,不需要了就刪掉,對其他部分毫無影響。
雖然用戶可以通過虛擬機還原軟件的原始環境。但是,這個方案有幾個缺點:
(1)資源佔用多
虛擬機會獨佔一部分內存和硬盤空間。它運行的時候,其他程序就不能使用這些資源了。哪怕虛擬機裏面的應用程序,真正使用的內存只有 1MB,虛擬機依然需要幾百 MB 的內存才能運行。
(2)冗餘步驟多
虛擬機是完整的操作系統,一些系統級別的操作步驟,往往無法跳過,比如用戶登錄。
(3)啓動慢
啓動操作系統需要多久,啓動虛擬機就需要多久。可能要等幾分鐘,應用程序才能真正運行。

Linux容器

由於虛擬機存在這些缺點,Linux 發展出了另一種虛擬化技術:Linux 容器(Linux Containers,縮寫爲 LXC)。
Linux 容器不是模擬一個完整的操作系統,而是對進程進行隔離。或者說,在正常進程的外面套了一個保護層。對於容器裏面的進程來說,它接觸到的各種資源都是虛擬的,從而實現與底層系統的隔離。
由於容器是進程級別的,相比虛擬機有很多優勢。
(1)啓動快
容器裏面的應用,直接就是底層系統的一個進程,而不是虛擬機內部的進程。所以,啓動容器相當於啓動本機的一個進程,而不是啓動一個操作系統,速度就快很多。
(2)資源佔用少
容器只佔用需要的資源,不佔用那些沒有用到的資源;虛擬機由於是完整的操作系統,不可避免要佔用所有資源。另外,多個容器可以共享資源,虛擬機都是獨享資源。
(3)體積小
容器只要包含用到的組件即可,而虛擬機是整個操作系統的打包,所以容器文件比虛擬機文件要小很多。

總之,容器有點像輕量級的虛擬機,能夠提供虛擬化的環境,但是成本開銷小得多。

Docker

Docker 屬於 Linux 容器的一種封裝,提供簡單易用的容器使用接口。它是目前最流行的 Linux 容器解決方案。
Docker 將應用程序與該程序的依賴,打包在一個文件裏面。運行這個文件,就會生成一個虛擬容器。程序在這個虛擬容器裏運行,就好像在真實的物理機上運行一樣。有了 Docker,就不用擔心環境問題。
總體來說,Docker 的接口相當簡單,用戶可以方便地創建和使用容器,把自己的應用放入容器。容器還可以進行版本管理、複製、分享、修改,就像管理普通的代碼一樣。

Docker 的主要用途,目前有三大類。
(1)提供一次性的環境。比如,本地測試他人的軟件、持續集成的時候提供單元測試和構建的環境。
(2)提供彈性的雲服務。因爲 Docker 容器可以隨開隨關,很適合動態擴容和縮容。
(3)組建微服務架構。通過多個容器,一臺機器可以跑多個服務,因此在本機就可以模擬出微服務架構。

Docker 把應用程序及其依賴,打包在 image 文件裏面。只有通過這個文件,才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根據 image 文件生成容器的實例。同一個 image 文件,可以生成多個同時運行的容器實例。

image 是二進制文件。實際開發中,一個 image 文件往往通過繼承另一個 image 文件,加上一些個性化設置而生成。舉例來說,你可以在 Ubuntu 的 image 基礎上,往裏面加入 Apache 服務器,形成你的 image。

Docker的基本組成部分

在Docker中,常用到的基本組成:

  • Docker主機(Host): 安裝Docker程序的機器(Docker直接安裝在操作系統之上),一般爲Linux系統。
  • Docker客戶端(Client):成功連接Docker主機能進行操作,例如能成功訪問Linux系統的XShell MobaXterm等。
  • Docker倉庫(Repository):用來保存各種打包好的軟件鏡像。
  • Docker鏡像(Image):軟件打包好的鏡像;放在docker倉庫中;
  • Docker容器(Container):鏡像啓動後的實例稱爲一個容器;容器是獨立運行的一個或一組應用

這裏 重點對後面三個概念進行論述。理解了倉庫,鏡像和容器,就理解了 Docker 的整個生命週期。

鏡像Image

操作系統分爲內核和用戶空間。對於 Linux 而言,內核啓動後,會掛載 root 文件系統爲其提供用戶空間支持。而 Docker 鏡像(Image),就相當於是一個 root 文件系統

Docker 鏡像是一個特殊的文件系統,除了提供容器運行時所需的程序、庫、資源、配置等文件外,還包含了一些爲運行時準備的一些配置參數(如匿名卷、環境變量、用戶等)。鏡像不包含任何動態數據,其內容在構建之後也不會被改變。

因爲鏡像包含操作系統完整的 root 文件系統,其體積往往是龐大的,因此在 Docker 設計時,就充分利用 Union FS 的技術,將其設計爲分層存儲的架構。所以嚴格來說,鏡像並非是像一個 ISO 那樣的打包文件,鏡像只是一個虛擬的概念,其實際體現並非由一個文件組成,而是由一組文件系統組成,或者說,由多層文件系統聯合組成。

鏡像構建時,會一層層構建,前一層是後一層的基礎。每一層構建完就不會再發生改變,後一層上的任何改變只發生在自己這一層。比如,刪除前一層文件的操作,實際不是真的刪除前一層的文件,而是僅在當前層標記爲該文件已刪除。在最終容器運行的時候,雖然不會看到這個文件,但是實際上該文件會一直跟隨鏡像。因此,在構建鏡像的時候,需要額外小心,每一層儘量只包含該層需要添加的東西,任何額外的東西應該在該層構建結束前清理掉。

分層存儲的特徵還使得鏡像的複用、定製變的更爲容易。甚至可以用之前構建好的鏡像作爲基礎層,然後進一步添加新的層,以定製自己所需的內容,構建新的鏡像。

容器Container

鏡像(Image)和容器(Container)的關係,就像是面向對象程序設計中的 類 和 實例 一樣,鏡像是靜態的定義,容器是鏡像運行時的實體。容器可以被創建、啓動、停止、刪除、暫停等。

容器的實質是進程,但與直接在宿主執行的進程不同,容器進程運行於屬於自己的獨立的命名空間。因此容器可以擁有自己的 root 文件系統、自己的網絡配置、自己的進程空間,甚至自己的用戶 ID 空間。容器內的進程是運行在一個隔離的環境裏,使用起來,就好像是在一個獨立於宿主的系統下操作一樣。這種特性使得容器封裝的應用比直接在宿主運行更加安全。也因爲這種隔離的特性,很多人初學 Docker 時常常會混淆容器和虛擬機。

前面講過鏡像使用的是分層存儲,容器也是如此。每一個容器運行時,是以鏡像爲基礎層,在其上創建一個當前容器的存儲層,我們可以稱這個爲容器運行時讀寫而準備的存儲層爲 容器存儲層。

容器存儲層的生存週期和容器一樣,容器消亡時,容器存儲層也隨之消亡。因此,任何保存於容器存儲層的信息都會隨容器刪除而丟失。

按照 Docker 最佳實踐的要求,容器不應該向其存儲層內寫入任何數據,容器存儲層要保持無狀態化。所有的文件寫入操作,都應該使用 數據卷(Volume)、或者綁定宿主目錄,在這些位置的讀寫會跳過容器存儲層,直接對宿主(或網絡存儲)發生讀寫,其性能和穩定性更高。

數據卷的生存週期獨立於容器,容器消亡,數據卷不會消亡。因此,使用數據卷後,容器刪除或者重新運行之後,數據卻不會丟失。

倉庫Repository

鏡像構建完成後,可以很容易的在當前宿主機上運行,但是,如果需要在其它服務器上使用這個鏡像,我們就需要一個集中的存儲、分發鏡像的服務,Docker Registry 就是這樣的服務。

一個 Docker Registry 中可以包含多個 倉庫(Repository);每個倉庫可以包含多個 標籤(Tag);每個標籤對應一個鏡像。

通常,一個倉庫會包含同一個軟件不同版本的鏡像,而標籤就常用於對應該軟件的各個版本。我們可以通過 <倉庫名>:<標籤> 的格式來指定具體是這個軟件哪個版本的鏡像。如果不給出標籤,將以 latest 作爲默認標籤。

以 Ubuntu 鏡像 爲例,ubuntu 是倉庫的名字,其內包含有不同的版本標籤,如,16.04, 18.04。我們可以通過 ubuntu:16.04,或者 ubuntu:18.04 來具體指定所需哪個版本的鏡像。如果忽略了標籤,比如 ubuntu,那將視爲 ubuntu:latest。

倉庫名經常以 兩段式路徑 形式出現,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用戶環境下的用戶名,後者則往往是對應的軟件名。但這並非絕對,取決於所使用的具體 Docker Registry 的軟件或服務。

Docker Registry 公開服務

Docker Registry 公開服務是開放給用戶使用、允許用戶管理鏡像的 Registry 服務。一般這類公開服務允許用戶免費上傳、下載公開的鏡像,並可能提供收費服務供用戶管理私有鏡像。

最常使用的 Registry 公開服務是官方的 Docker Hub,這也是默認的 Registry,並擁有大量的高質量的官方鏡像。除此以外,還有 CoreOS 的 Quay.io,CoreOS 相關的鏡像存儲在這裏;Google 的 Google Container Registry,Kubernetes 的鏡像使用的就是這個服務。

由於某些原因,在國內訪問這些服務可能會比較慢。國內的一些雲服務商提供了針對 Docker Hub 的鏡像服務(Registry Mirror),這些鏡像服務被稱爲加速器。常見的有 阿里雲加速器、DaoCloud 加速器 等。使用加速器會直接從國內的地址下載 Docker Hub 的鏡像,比直接從 Docker Hub 下載速度會提高很多。在 安裝 Docker 一節中有詳細的配置方法。

國內也有一些雲服務商提供類似於 Docker Hub 的公開服務。比如 時速雲鏡像倉庫、網易雲鏡像服務、DaoCloud 鏡像市場、阿里雲鏡像庫 等。

私有 Docker Registry

除了使用公開服務外,用戶還可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 鏡像,可以直接使用做爲私有 Registry 服務。在 私有倉庫 一節中,會有進一步的搭建私有 Registry 服務的講解。

開源的 Docker Registry 鏡像只提供了 Docker Registry API 的服務端實現,足以支持 docker 命令,不影響使用。但不包含圖形界面,以及鏡像維護、用戶管理、訪問控制等高級功能。在官方的商業化版本 Docker Trusted Registry 中,提供了這些高級功能。

除了官方的 Docker Registry 外,還有第三方軟件實現了 Docker Registry API,甚至提供了用戶界面以及一些高級功能。比如,Harbor 和 Sonatype Nexus。

Docker安裝

這裏,我們在CentOS7上進行Docker操作,Docker的安裝過程比較簡單。

  1. 首先檢查Linux系統內核版本,最好是選擇3.10及以上的版本。
    在這裏插入圖片描述
  2. yum 安裝docker
    yum install docker
    
    這裏yum安裝 需要root權限,普通用戶先選擇su或者sudo轉變爲root用戶或者得到root權限,然後yum安裝
  3. 啓動docker
    systemctl start docker
    
  4. 停止Docker
    systemctl stop docker
    

爲了開發者方便,可以開機自啓動Docker:

	systemctl enable docker

Docker的常用命令和基本操作

鏡像Image操作

在Docker中運行某項服務,例如tomcat,MySQL,我們首選需要下載相關的鏡像文件,一般我們都會在Docker hub上搜索相關鏡像文件。例如:搜索MYSQL
在這裏插入圖片描述
在docker客戶端中常用的命令如下:
在這裏插入圖片描述
在docker拉取鏡像的時候,國外網站因爲衆所周知的一道牆下載速度慢,我們這裏選擇Docker配置阿里雲鏡像進行加速。具體的方法:

  1. 進入阿里雲的容器鏡像服務:容器鏡像服務
    在這裏插入圖片描述

容器Container操作

  1. 下載得到相關的鏡像文件後,我們可以根據鏡像啓動容器(例如,我們啓動一個Tomcat):
    docker run --name mytomcat -d tomcat
    
  2. 查看運行中的容器
    docker ps  
    
  3. 停止運行中的容器
    docker stop  容器的id(CONTAINER ID)
    
  4. 查看所有的容器(包括正在運行的 和停止運行的)
    docker ps -a
    
  5. 啓動之前啓動過現在停止的容器
    docker start 容器id
    
  6. 刪除一個容器
    docker rm 容器id
    
  7. 進入目標container中
    docker exec -it 20de4f9635a5 /bin/bash
    
  8. 退出當前docker容器,使用exit

運行Tomcat,MySQL遇到的坑

  1. docker安裝tomcat啓動之後,window主機訪問報404錯誤
    我們知道,在啓動docker中的服務時,需要將其啓動的端口號和linux服務器的端口好進行綁定,外界才能通過訪問Linux的ip地址+端口號才能訪問。所以在docker啓動tomcat時,我們需要進行端口的映射操作:

    docker run -d -p 8888:8080 tomcat
    

    使用docker ps 可以成功看到tomcat進程被成功啓動,但是在瀏覽器訪問tomcat時常出現報404錯誤: 在這裏插入圖片描述
    這裏的問題在於,docker啓動的tomcat容器目錄中webapps文件目錄下空。
    解決辦法如下:
    首先,進入到tomcat目錄中

    docker exec -it 目標容器ID /bin/bash
    

    在這裏插入圖片描述
    發現webapps爲空,tomcat的默認項目資源都在webapps.dist文件夾下。
    將webapps刪除,將webapps.dist重命名爲webapps。

    rm -rf webapps
    mv webapps.dist webapps
    

    之後在瀏覽器中訪問tomcat,可看到tomcat正常啓動。
    在這裏插入圖片描述

  2. 本地不能正常連接docker啓動的MYSQL
    首先,docker上啓動MySQL
    錯誤的啓動方式

    docker run --name mysql01 -d mysql
    

    這種方式啓動,mysql會異常退出。查看docker ps時搜索不到剛纔啓動的mysql進程。
    通過docker ps -a查看到啓動失敗的mysql,然後查看相關的錯誤日誌。

    [root@m1 ~]# docker logs 42f09819908b
    error: database is uninitialized and password option is not specified 
    You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD 		  and MYSQL_RANDOM_ROOT_PASSWORD;
    

    意思是MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD;這個三個參數必須指定一個。也就是在實際運用中,我在啓動mysql的時候需要指定用戶(root)的密碼。

    修改後的啓動方式:

    [root@m1 ~]# docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
    b874c56bec49fb43024b3805ab51e9097da779f2f572c22c695305dedd684c5f
    [root@localhost ~]# docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
    b874c56bec49        mysql               "docker-entrypoint.sh"   4 seconds ago       Up 3 seconds        3306/tcp            mysql01
    

    但是這樣依舊不能被外部訪問,因爲docker容器沒有做linux主機的端口映射,所以正確的啓動方式是:

    [root@m1 ~]# docker run -p 3306:3306 --name mysql02 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
    ad10e4bc5c6a0f61cbad43898de71d366117d120e39db651844c0e73863b9434
    [root@localhost ~]# docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
    ad10e4bc5c6a        mysql               "docker-entrypoint.sh"   4 seconds ago       Up 2 seconds        0.0.0.0:3306->3306/tcp   mysql02
    

    這裏有時候會報錯 顯示3306端口已經被佔用:
    在這裏插入圖片描述
    南國的解決辦法是查找到當前Linux的使用端口,將其端口刪掉。然後重新啓動MySQL:
    在這裏插入圖片描述
    成功啓動MySQL後,本機使用Navicat連接數據庫,在查看MySQL成功運行,並且Navicat連接數據庫的連接屬性正確後。連接時報錯1251。如下圖:
    在這裏插入圖片描述
    這裏的解決辦法時重置MySQL的密碼:

  • 進入mysql客戶端
  • 輸入用戶密碼進入mysql數據庫
  • 重新設置密碼
    在這裏插入圖片描述
    在這裏插入圖片描述
    密碼修改成功後,使用Navicat重新連接:
    在這裏插入圖片描述
    參考文獻:
    https://yeasy.gitbooks.io/docker_practice/basic_concept/repository.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章