由淺入深 docker 系列: (3) docker-compose

lixiang9194發表於2018-11-29

在第二篇文章中,我們學會了使用 dockerfile 構建 docker 映象,看起來已經能夠滿足我們的日常需求了。無論需要什麼環境,在 dockerfile 裡逐步構建,然後 build、run,就 ok 了,也滿足了我們docker 隔離性、快速部署的要求,那為什麼還會有這一節?
來看一個網站開發最常見的場景:我們要有資料庫,網站應用,nginx,互相配合才是完整的環境。是的,我們完全可以以 ubuntu 為基礎映象,把這些一股腦全裝進去,然後執行。但是這樣有很多缺點,比如我們每次都要重新裝 mysql 而不是直接利用 mysql 官方的基礎映象,升級維護不方便;如果我們的應用要擴充套件也很難,因為每個應用都連線的自己內部的資料庫,無法共享資料;事實上,這種方式是典型的虛擬機器的使用方式,不是 docker 的正確開啟方式。
docker 是輕量化的應用程式,docker 官方推薦每個 docker 容器中只執行一個程式(下篇文章你將明白這是為什麼),那麼就是說,我們需要分別為我們的應用、資料庫、nginx 建立單獨的 docker 容器,然後分別啟動它。想象一下,構建好 docker 之後,每次啟動我們的網站,都要至少 docker run 三次,是不是很繁瑣?而且此時這幾個 docker 是分散獨立的,很不方便管理。既然這幾個 docker 都是為了同一個網站服務,是不是應該把它們放到一起?這就引出了 docker-compose 專案。
docker-compose是 docker 官方的開源專案,使用 python 編寫,實現上呼叫了 Docker 服務的 API 進行容器管理。其官方定義為為 「定義和執行多個 Docker 容器的應用(Defining and running multi-container Docker applications)),其實就是上面所講的功能。


一:安裝
預設情況下,windows 和 mac 下的 docker 已經自帶了 docker-compose 工具,可以使用 docker-compose -v 命令檢視。

對於 linux 系統,需要自己手動安裝,可以直接下載二進位制檔案安裝,參考官方文件,更酷的是,可以直接使用 pip 安裝,甚至可以使用 docker-compose 容器(比較複雜,不推薦~)。

二:簡介
類似 docker 的Dockerfile檔案,docker-compose使用 YAML 檔案對容器進行管理。
對於 docker-compose 有兩個基本的概念:
服務(service):一個應用容器,即 docker 容器,比如之前所說的mysql 容器、nginx 容器
專案(project):由一組關聯的應用容器組成的一個完整業務單元,比如上面所講的由 mysql、web app、nginx 容器組成的網站。docker-compose 面向專案進行管理。
再簡單說下 YAML 檔案格式。

  • 大小寫敏感,縮排表示表示層級關係
  • 縮排空格數不重要,相同層級左側對齊即可。(不允許使用 tab 縮排!)
  • 由冒號分隔的鍵值對錶示物件;一組連詞線開頭的行,構成一個陣列;字串預設不使用引號
    這些基本夠我們使用了,詳細的格式說明可參考這篇YAML 語言教程-阮一峰。

三:構建
接下來我們使用 docker-compose 構建一個php 網站專案,並逐步講解其使用。
我們需要一個網站專案,這裡以 summerblue 的larabbs論壇系統為例,這個專案使用了 mysql、redis,我們可以驗證搭建是否成功。

  1. 從遠端倉庫把其克隆到本地,然後開始我們今天的工作。

  2. 在專案資料夾下建立 docker-compose.yml檔案。

  3. 先在 docker-compose.yml 檔案裡新增如下程式碼,構建我們的 php 應用。

    version: '2'
    services:
    
        # our web application
        app:
            build:
                context: ./
                dockerfile: app.dockerfile
            volumes:
                - ./:/var/www
            working_dir: /var/www
            environment:
                - DB_HOST=database
                - REDIS_HOST=redis

    下面解釋下我們的程式碼。

    • version: 表示我們的compose檔案的版本,目前有1,2,3,每個版本語法不盡相同,這裡以版本2為例。
    • services: 即我們要開始定義服務,每個docker容器為一個服務。
    • app: 這裡我們定義了第一個服務,app 為其名字
    • build: 指定該容器構建引數
    • volumes: 與 dockerfile 中 -v 引數相似,這裡是將當前資料夾掛載到容器的/var/www 目錄下
    • working_dir: 指定容器工作目錄
    • environment: 設定環境變數。由於 laravel 框架在環境變數已有值的情況下不會載入.env 配置,這裡 DB_HOST和 REDIS_HOST 就是.env 檔案中配置資料庫連線的引數,我們設定它以便連線docker 的資料庫,database 和 redis 是接下來定義的服務名稱。

    這是 app.dockerfile 檔案的內容,之前都講過,不再細說。

    FROM php:7.1.22-fpm
    
    # 安裝必要的 php 依賴包
    RUN apt-get update \
        && apt-get install -qq git curl libmcrypt-dev libjpeg-dev libpng-dev libfreetype6-dev libbz2-dev \
        && apt-get clean
    
    # 安裝 php 擴充套件
    RUN docker-php-ext-install pdo pdo_mysql mcrypt zip gd
  4. 建立一個 composer 服務以安裝 composer 依賴,docker-compose.yml 檔案新增以下內容
    # install dependencies
        composer:
            image: prooph/composer:7.1
            volumes:
                - ./:/var/www
            working_dir: /var/www
            command: install

注意這裡同樣將當前資料夾掛載到了容器中,因此對檔案所做的更改都直接作用於本地檔案,而我們的 app 容器也掛載了當前資料夾,這裡安裝的包檔案都能生效。

  1. 建立一個 nginx 服務,docker-compose.yml 檔案新增以下內容
    # web server
        nginx:
            build:
                context: ./
                dockerfile: nginx.dockerfile
            volumes:
                - ./public:/var/www/public
            ports:
                - 80:80
* 這裡將 public 資料夾掛載到了容器中, nginx 直接返回了靜態檔案,否則你將看到網站格式亂了,因為獲取不到 css 等檔案,如果是單純後端可以不掛載此資料夾
* ports 將nginx容器的80埠對映到本機80埠

nginx.dockerfile 檔案內容,為了新增預設配置檔案

```
FROM nginx:1.10
ADD vhost.conf /etc/nginx/conf.d/default.conf
```

vhost.conf 檔案,nginx 配置檔案

```
server {
    listen 80;
    server_name www.larabbs.test
    index index.php index.html;
    root /var/www/public;

    error_log /var/log/nginx/error.log notice;
    access_log /var/log/nginx/access.log main;

    location / {
        try_files $uri /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}
```
* 這裡配置了 server_name,因為 larabbs 使用了sudo-su包,不配置域名訪問會有許可權問題。
* fastcgi_pass app:9000 這裡將動態請求轉發給了 app 容器的9000埠,即 php-fpm 服務的埠,預設 docker-compose 同一專案下的容器是在同一個網路中,無需對映可以直接互相訪問。
  1. 建立資料庫服務,docker-compose.yml 檔案新增以下內容

    # mysql database
        database:
            image: mysql:5.7
            environment:
              - "MYSQL_ROOT_PASSWORD=root"
              - "MYSQL_DATABASE=larabbs"
              - "MYSQL_USER=homestead"
              - "MYSQL_PASSWORD=secret"
    
        # redis database
        redis:
            image: redis:5
    • 這裡 database 和 redis 是服務名,也即在 app 服務的 environment 變數設定的引數,因此 app 才能訪問到我們容器中的資料庫,當然你也可以使用其它名字。
    • environment 這裡我們設定了 mysql 的資料庫及使用者名稱密碼,即是 env 檔案中配置的使用者名稱密碼,mysql 容器會自動初始化,然後應用才有許可權連線。

    到目前為止配置檔案已經寫完了,完整的檔案可以在我的 github 看到。

四:使用

  1. 啟動專案
    docker-compose 最常用的命令就是 docker-compose up 了,該命令十分強大,它將嘗試自動完成包括構建映象,(重新)建立服務,啟動服務,並關聯服務相關容器的一系列操作。
    因此,在專案資料夾下執行此命令。注意要在專案資料夾下,否則 docker-compose 找不到 docker-compose.yml 檔案,也不知道你是如何配置的。
    時間可能較長,請耐心等待。如果遇到網路故障,可以重試。
    簡單介紹下輸出:


可以看到 此時app 服務已啟動,等待連線。


redis 服務已啟動。


database 服務已啟動,在3306埠等待連線。


composer 服務準備開始安裝依賴。


composer 服務完成任務退出。

當然順序可能不同,但正常情況下nginx、mysql、redis、app 服務都已經啟動,執行正常。
另外,你可以給命令加上-d 引數,以忽略輸出,當然第一次執行還是仔細觀察輸出為好,將來使用時可以這麼做。
  1. 專案使用
    首先執行下 docker ps,可以看到目前有4個容器,容器名都加上了專案字首。

    此時你可以使用 docker exec 命令進入相應容器執行初始化操作,因為 docker-compose 本身也是呼叫的 docker api,但是既然使用了 docker-compose 來管理專案,我們必然有更方便的方式。

    執行 docker-compose ps,可以至檢視當前專案的容器狀態。

    可以看到,四個服務正在執行,而 composer 服務已經執行完任務退出。
    如果要容器執行命令,直接 docker-compose exec service_name command 更方便。
    比如,進入我們的 nginx 容器,nginx 即是 YAML 檔案裡定義的服務名。

    接下來,我們進行網站的初始化工作。即生成祕鑰、初始化資料庫等。
    app 也是 YAML 檔案裡定義的服務名。

    一切都順利進行。
    對了,因為上面提到 sudo-su的原因,你必須使用域名訪問網站,在 host 檔案新增 larabbs.test到127.0.0.1的對映即可。
    然後,你應該就可以正常訪問 http://larabbs.test 網站了。

  2. 容器的停止
    直接 control-c 或者 docker-compose stop 即可。
    注意下次docker-compose up 預設仍會繼續使用之前的容器和資料。
    必要時你可以新增--build 引數重新構建映象,或者--force-recreate引數重新建立容器。

更多資料:
docker 三劍客-compose
docker-compose 官方文件


閒言
這個系列寫了三篇了,算是把 docker 的基本使用介紹完了,更準確的說,應該是把我會用的介紹完了。因為本身是開發工程師,更著重於日常開發過程中使用,經驗不多,對於運維視角的 docker不甚瞭解,而 docker 更大的意義應該是在運維層,所以有介紹的不合適甚至錯誤的地方,歡迎指出,或者分享你對 docker 的理解與使用。
另外,接下來會介紹 docker 與虛擬機器的區別,docker 的原理等,寫起來會比較難,但我會盡快完成。相信看了之後,會對 docker 的實質有更深的理解,就算是吹牛逼也會更有自信,歡迎關注~

原文見我的知乎專欄

相關文章