Day 1 在本機執行 Laravel container
2024 寫在最前面:這是去年(2023)IT 鐵人賽的文章,來個舊文重貼~
開賽啦~開賽啦~這 30 天會帶大家從地面飛上雲端!呃不是,是從本機建立 Laravel 的 Docker image 開始,一步步透過 Gitlab Pipeline 建立將 image deploy 上 AWS 的 CD 流程,再利用 AWS 的 container 服務 ECS 來運行 Laravel 的 web application。
另外,作為工程師,懶是最大的美德,能電腦做的事情就不要自己做,能用程式跑的東西就不要自己手動按,所以我們也會使用 infrastructure as code 的工具 Terraform 來維護以及持續進化各項基礎建設。(重點是雲裡那麼多複雜的設定,沒有程式碼筆者根本記不起來)
這 30 天會使用 Linux 作為主要操作平台,各種指令與工具等等都以 Linux 為主。使用到的主要工具都是跨平台的,Windows 跟 MAC 原則上都能使用,只是文章會以 Linux 為主說明,用其他平台的話可能需要自己摸索工具如何安裝、設定檔在哪裡等等。
我們會從本機 build docker image、啟動 container 開始,一路設定 Gitlab、使用 AWS web console 以及 command line 工具建立各項基礎建設,再到使用 terraform 並進一步讓我們的雲端架構達到 high availability。因為起點是 Linux、Gitlab 跟 Docker,讀者需要對 Linux、Git 以及 Docker 有基本了解,像是 Docker 知道有 image、image 可以拿來跑 container 以及 Docker Hub 是什麼就夠了。
開始前的提醒:這 30 天內會使用 AWS 的服務,如果讀者沒有用過 AWS,可以申請帳號、利用一年的免費使用時間。如果讀者已經超過一年的免費使用期,請注意:照著本系列文章操作,是會有費用產生的,筆者在範例中盡可能用費用較低的資源,但是讀者須自行注意資源的使用情形與花費。(講到 AWS 會再提醒一次並說明如何使用 AWS Budgets 功能)
行前最後提醒:這個旅程……不會很順利,各位會看到 DevOps 充滿青春與汗水(??)的日常生活。
好!那就讓我們從在本機 build 出 Laravel 的 Docker image 開始吧~
Base Docker Image 介紹
我們這裡要使用的是 Ernest Chiang 的 nginx + php-fpm docker image 作為 base image(感謝大大!)這是一個把 nginx 跟 php-fpm 結合起來的 docker image,我們只要把 Laravel 的程式碼跟一些相關設定放進去,就是個可以運行的 image 了!
這個 image 是以 supervisor 來啟動及監控 nginx、php-fpm worker 等 process。supervisor 會維持設定的 process 數量,發現 process 被(各種原因)關掉的話會重新啟動它。這麼做才能確保我們的 container 一直有執行著需要的 process,不會因為 process 當掉之類的問題就整個無法正常提供服務。
啟動 MySQL container
有了 Web Server 跟 PHP 後,絕大多數情況都需要的還有 Database。為了讓 Laravel 可以順利執行,我們先隨便用 docker 準備個 MySQL database 給它連:
1 | $ sudo docker run -d \ |
這個指令翻成白話文:在背景啟動一個 MySQL 8.0 的 container 叫做 dbserv,並且以環境變數允許帳號的密碼是空的、預設 database 叫做 laravel。
允許空密碼、使用 default database 作為 laravel 的 database 只是為了實驗而簡化的作法,一般環境不建議這麼使用!(好孩子不要偷懶
啟動 container 後可以用 sudo
docker ps
看到執行中的 container 們:
1 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES |
為了設定 Laravel 的 database 連線資訊並且降低範例的麻煩複雜度,我們直接用 container 的 IP 來連線,所以用 sudo docker inspect dbserv
來查 dbserv 這個 container 的 IP address。
1 | $ docker inspect dbserv |
在 Networks block 看到 IP address 是172.17.0.3
,所以我們在 .env
的 DATABASE 相關設定要設成:
1 | DB_CONNECTION=mysql |
Dockerfile 及其快樂夥伴們
接下來看看 docker image 的靈魂:Dockerfile!每行指令的意思直接標注在 comment,完整程式碼在 Gitlab 。
1 | # 使用 Ernest Chiang 的 nginx + php-fpm docker image 作為 base image |
Docker image 的 flavor
很多 docker image 後面會接像是 -buster
、-bullseye
、-slim
或 -alpine
的 suffix,這些 suffix 表示不同的 image flavor(口味?),也就是它們基於什麼 Linux distribution 而來或者有什麼特性。
常見的口味(?)有:
沒有 suffix 的 full official image:建立在最新的 stable Debian 上
buster/bullseye
等等 Debian codename:建立在某個版本的 Debian 上。slim
:從 full image 瘦身而來的 image。alpine
:以 Alpine Linux 為基礎的 image,優點是 image 小,但可能需要處理相容性問題。
docker/nginx/default.conf
docker/nginx/default.conf
是 nginx 的設定,使用 Laravel 官方設定 做兩處修改:
root /var/www/html/public;
改到 image 放 Laravel code 的路徑fastcgi_pass 127.0.0.1:9000;
因為我們以 php-fpm 預設方式啟動,它會聽 localhost 的 port 9000 來收 request (ref),所以我們要在 nginx 把 request 傳過去。
1 | server { |
docker/laravel-worker.conf
前面說到 image 是用 supervisor 把各種 process 執行起來並且監控它們,所以我們也用 supervisor 把 Laravel worker 執行起來,以下是 worker 用的 supervisor 設定:
1 | [program:laravel-worker] |
大致來說是以 user www-data
來執行 8 個 process,會把 log 記錄在 /var/www/html/worker.log
,會自動重啟等等。
docker/docker-entrypoint.sh
entry script 是從 source 複製來的,後面加入跟 Laravel 有關的啟動 crontab、執行 migration、做各種 cache 的指令。
1 |
|
Build image
寫好 Dockerfile 就是要編 image 啦~我們以目前目錄作為 context 來 build image,context 可以想成建立 image 的環境,通常會是 Dockerfile 的所在地。用 -t
給 image 上 my-app
的 tag,之後可以用來在操作中指定 image (docker build 指令 ref):
1 | $ docker build . -t my-app |
Run container
build 好 image 後,終於要啟動 container 看看能不能成功執行 Laravel:
1 | $ sudo docker run -it -p 8000:80 --name app my-app |
這裡我們以 tag 為 my-app
的 docker image 建立並啟動 container,將 container 命名為 app
,並且讓我們可以透過 console 直接操作 container。另外,把本機的 port 8000 對應到 container 的 port 80,如此對應後存取本機 port 8000 等同存取 container 的 port 80,也就是一般 web server http 使用的 port。(docker run 指令 ref)
啟動 container 後會看到 entry script 執行的 output:
如果中間有任何錯誤也會顯示出來並且(大多數狀況下)container 會停止執行,最常遇到的是各種 Laravel 相關的錯誤,諸如連不上 database、沒有 app key 等等。我們的 container 看起來挺正常的,用瀏覽器連上 http://127.0.0.1:8000
看看吧!
是 Laravel 的歡迎畫面!成功啦!
停止 & 清除 container
執行完 container 要停止有幾個方法:
在上面 container 的 console 畫面直接 ctrl + c 結束它
使用指令
docker stop
app
這裡的app
是 container 的名稱,也可以用 container id
已經關閉的 container 要再啟動執行可以用 docker start
[CONTAINER NAME or ID]
。如果想刪除一個 container,關閉(stop)它後用 docker rm
[CONTAINER NAME or ID]
。