用DOCKER在本機建立MAGENTO (Docker compose 篇)

續前篇,這篇是為了解釋一下docker-compose 這部份

version: '3' # 版本號 
services: 
  db:
    hostname: db.localhost
    container_name: magento-db
    image: 'mariadb:10.3'
    environment:
      - MYSQL_ROOT_PASSWORD=magento2
      - MYSQL_DATABASE=magento2
      - MYSQL_USER=magento2
      - MYSQL_PASSWORD=magento2
    ports:
      - 3306:3306
    volumes:
      - 'magento-project:/var/lib/mysql'
    networks:
      magento:
        aliases:
          - db.localhost
    healthcheck:
      test: 'mysqladmin ping -h localhost -pmagento2'
      interval: 30s
      timeout: 30s
      retries: 3           
  web: 
    hostname: web.localhost
    container_name: magento-nginx
    image: magento/magento-cloud-docker-nginx:1.19-1.3.5
    ports:
      - 80:80
      - 443:443
    volumes:
      - ".:/app"
    networks:
      magento:
        aliases:
          - web.localhost  
    depends_on:
      fpm:
        condition: service_started
  elasticsearch:
    hostname: es.localhost
    container_name: magento-elasticsearch
    image: magento/magento-cloud-docker-elasticsearch:7.11-1.3.5
    ports: 
      - '9200:9200'
      - '9300:9300'
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      magento:
        aliases:
          - es.localhost  
  fpm:
    hostname: php.localhost
    container_name: magento-php
    image: magento/magento-cloud-docker-php:8.1-fpm-1.3.5
    volumes:
      - '.:/app'
    environment:
      - MAGENTO_RUN_MODE=developer
      - 'PHP_EXTENSIONS=bcmath bz2 calendar exif gd gettext intl mysqli pcntl pdo_mysql soap sockets sysvmsg sysvsem sysvshm opcache zip redis xsl blackfire sodium'
    depends_on:
      db:
        condition: service_healthy
    networks:
      magento:
        aliases:
          - php.localhost    
  
networks:
  magento:
    driver: bridge
volumes:
  magento-project: {}

一開始接觸docker-compose 會對這個第一印象是 “這是甚麼來的?????”

也許心裡會覺得很麻煩,很抗拒,的確,如果有心去學習的話可以到官方網頁多看一下

https://docs.docker.com/compose/compose-file/

可是有時文件太多,要快速明白的話,就只有邊做邊學好了,因為讀過了未必明白,也記不上 。

首先要明白的是,這是一個yaml檔案,格式是以yaml作compile, 如發現改時出現錯誤有可能是因為格式錯誤,可以把設定放到下列網址作驗證

https://www.yamllint.com/

而如何讀這個檔案呢?

version: '3' 

這個比較易明,version是一個特別字,用來通知docker compose 執行時,這個文檔是以version 3 格式來編寫

networks:
  magento: #這個名稱可以自定義
    driver: bridge

Networks 是一個用來連結各服務的網絡,跟現實一樣,因為服務與服務之間需要溝通,有了NETWORK,NGINX才可以連到PHP去處理網站收到的要求,PHP如要顯示產品資料就要連到DATABASE去,所以需要建立networks,magento這個名是自定義的,稍後會用來設定到服務,driver 是用來設定NETWORK要用哪一種形式去做連結,bridge 就是比較直接,好比一條橋連結各項服務 ,其他的driver則是 overlay, 等等,因為本次針對建立MAGENTO,所以不會詳細說明這部份

volumes:
  magento-project: {} #這個名稱可以自定義 

volumes是一個用來存取DOCKER儲存的資料,好像database, 當docker的服務運作時會把儲存的資料暫存到IMAGE內,但服務RESTART後便會清理掉,如關機重開等狀況就會出現資料清除無法挽回的狀況,所以VOLUME就是設定,把IMAGE內暫存的資料指向DOCKER外的一個FOLDER,你便可以在外部得到這份資料,當docker restart 時IMAGE不會因為重建CONTAINER而流失原本的資料

services:

這部份就會是一直以來用來設定各項服務的位置

db: # 這個是可自定義的
    hostname: db.localhost # 一個DOCKER內部用的服務主機名稱
    container_name: magento-db 
    image: 'mariadb:10.3'
    environment: # 環境變數,不同的IMAGE有不同的設定,可到hub.docker.com參考 
      - MYSQL_ROOT_PASSWORD=magento2 
      - MYSQL_DATABASE=magento2
      - MYSQL_USER=magento2
      - MYSQL_PASSWORD=magento2
    ports: # 連接埠 
      - 3306:3306
    volumes: # 把存取的資料指向DOCKER外部哪個地方,可設定多個地方
      - 'magento-project:/var/lib/mysql'
    networks:
      magento:
        aliases:
          - db.localhost

db: 這個是服務的名稱,盡可能用人可以理解的名稱,而在docker內部另外的服務如要連到這個服務進行存取,則可以在HOST設定使用這個名稱為變數的值,例如PHP要連到db,可以用以下語法

<?php
$dsn = 'mysql:dbname=testdb;host=db';
$user = 'dbuser';
$password = 'dbpass';

$dbh = new PDO($dsn, $user, $password);

hostname: 是這個服務在DOCKER內部用,如要跟其他服務互相溝通時需要在networks處加入aliases:

container_name: 這個是給container的名稱,如沒有設定會設定成現在 這個文件夾名稱_CONTAINER名稱_1

image: 是這個服務器的影像檔

environment: 因應不同影像檔會有不同的設定,例如這個是一個DB的服務,DB需要一個用戶認證,否則任何人都可以進入DB拿取資料,所以需要設定一個管理用戶的PASSWORD,供用家連結,也會有一個DATABASE及可以行使這個DATABASE的用戶及登入密碼。

ports: docker其實是一個小世界,每個服務都是經PORT 連接埠去互常溝通,例如80是HTTP,443是HTTPS等,那如果我想在DOCKER外連入DOCKER內的服務,便需要在這裡進行設定,你會見到這裡

- 3306:3306

首先在 3306: 3306 前有一條橫線,在yaml裡這個是指一個多項設定,如在PHP就是 array(“3306:3306”), 這裡會見到有兩個3306, 由: 分隔,第一組是3306指的是外部連入時用的PORT,第二組3306指的是想指向這個服務的哪個PORT,當DB 起動時便會打開放3306 PORT供其他服務連結,而DOCKER內的DB也不例外,這個PORT只限內部使用,如果外部要連入去看資料 e.g. mysql workbench,則要有這個設定才可以連進去,而PORT一定不可以重覆,所以有其他PROJECT用到DOCKER時也需要連到DB,便可改一下第一個3306的設定

ports:
  - 13306:3306

這樣就是外部用13306 連到這個服務的3306 PORT去 ,但因為PORT有限,一旦發現很多服務要執行,便要小心處理重覆PORT這個問題

volumes:
      - 'magento-project:/var/lib/mysql'

volumes: 這個設定比較複雜,本身用途是把DOCKER外在的文件或文件夾指向該服務那個位置,但指向的位置是要先理解到該服務是把資料放在哪一個地方才可以作相應的設定,例如 mysql 是會把資料庫的所有資料放到/var/lib/mysql,為了防止container重啟時流失所有資料,便在docker處建立一個Volume名叫magento-project (自定義定名稱)這是一個實體位置在你的電腦內,然後當db 服務任何資料存到var/lib/mysql, 在那個實體位置都可以看到,如果你不太明白的話也可以把magento-project 轉成一個路徑 (PATH),absolute e.g. C:/Users/ 或 relative e.g. ../data 都可以

volumes:
      -  '../db:/var/lib/mysql' # 在這個docker-compose.yaml 的上一層建立一個db的文件夾,當服務建立時會見到該文件夾由空變成有檔案

當服務有任何存取你便會見到那些FILE在建立了及改動。

最後一段早前也有少量解釋

networks:
      magento:
        aliases:
          - db.localhost

因為PHP需要DB服務,所以早前設定了一個magento的network 以 bridge 模式 控各服務互常連結 ,這裡就是設定這個服務在magento network 內運行,以 db.localhost 作連結 ,(最後還是用 container name 作host name)

healthcheck:
      test: 'mysqladmin ping -h localhost -pmagento2'
      interval: 30s
      timeout: 30s
      retries: 3 

healthcheck: 這個是跟depend on 有關,可以想一下如果PHP這個服務是一個ESHOP,如果DB未準備好的話,PHP起動就在查詢商品資料便會出現問題,所以healthcheck就是通知所有DEPEND ON這個服務的其他服務,每個服務都有各自的TEST,db 就是要ping 一下可否連到連個服務,30秒內嘗試,最多重覆 3次,三次內能夠連結就回傳healthy 如果不能則按個別狀況作出回傳為何失敗 ,其他服務如發現其失敗便會等需不會開始運作或暫停。

下一個解讀的是

web: 
    hostname: web.localhost
    container_name: magento-nginx
    image: magento/magento-cloud-docker-nginx:1.19-1.3.5
    ports:
      - 80:80
      - 443:443
    volumes:
      - ".:/app"
    networks:
      magento:
        aliases:
          - web.localhost  
    depends_on:
      fpm:
        condition: service_started

了解了db的解讀,這個會更容易明,

這個服務叫web,當fpm服務起動過後,才用magento/magento-cloud-docker-nginx:1.19-1.3.5 的映像檔在magento networks運行,在同一個magento network內以web.localhost命名及跟其他服務溝通,文件的根目錄在/app,現時設定指向跟docker-compose.yaml 這個文檔的同層作稍後,比較特別的是fpm的container 必須是名叫fpm,如要改其他名稱便要寫成

depends_on:
  - fpm1:fpm #其他名叫fpm1, 

https://hub.docker.com/layers/magento/magento-cloud-docker-nginx/1.19-1.3.5/images/sha256-0b0284d5641cdffd6221d9037a93289647556987d9d3e0e9081b13401d53d847?context=explore

原因是因為這個檔內部設定了php part是指向名叫fpm container,所以如果發現運行不到時需要看一下用到的IMAGE有沒有預設

elasticsearch:
    hostname: es.localhost
    container_name: magento-elasticsearch
    image: magento/magento-cloud-docker-elasticsearch:7.11-1.3.5
    ports: 
      - '9200:9200'
      - '9300:9300'
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      magento:
        aliases:
          - es.localhost  

跟db及fpm 一樣,不同的是ulimits memlock set了 soft 及 hard 是 -1 , ulimit 是usage limit, 這裡是放任了mem沒有限制,因為soft 跟 hard set了-1 , soft 跟 hard 的意思是 limit 的容許度,soft的值一定要是較hard少,例如memlock soft 是 768 hard是1024, 如果memory用量過了 768 變成800, 當下系統會有一個內置TIMER開始運行並容許800用量持續一段預設時間,如果中途用量低至768或以下便會RESET這個TIMER,反之過了便會限制到768, HARD就不用說就是最高值。 而不設上限應是因為服務比較大食。(magento

fpm:
    hostname: php.localhost
    container_name: magento-php
    image: magento/magento-cloud-docker-php:8.1-fpm-1.3.5
    volumes:
      - '.:/app'
    environment:
      - MAGENTO_RUN_MODE=developer
      - 'PHP_EXTENSIONS=bcmath bz2 calendar exif gd gettext intl mysqli pcntl pdo_mysql soap sockets sysvmsg sysvsem sysvshm opcache zip redis xsl blackfire sodium'
    depends_on:
      db:
        condition: service_healthy
    networks:
      magento:
        aliases:
          - php.localhost  

最後就是fpm, 這個服務要等db服務起動才會建立,以magento/magento-cloud-docker-php:8.1-fpm-1.3.5的映像檔在magento network運行,內部以php.localhost為名與其他服務溝通,因為服務根目錄在/app,現指向這個docker-compose.yaml同一個目錄下執行magento的code, 環境變數是設定MAGENTO以developer mode運行,因為magento有 default, developer 跟 production 三個mode, 並要求php開啟並各項extension 功能供magento 使用

現在設定好了這個docker-compose.yaml 後只要運行以下指令

docker-compose up -d 

就會開始下載IMAGE並運行起來,但這時MAGENTO方面還要再花點時間安裝,會在下一篇解說