CI/CD 在整個開發流程中算是最後的環節,如果公司大一些有 MIS/DevOps 部門,這一塊通常會有專業人士處理;工程師寫完程式後只要 merge master branch,理論上就會自動觸發 CI/CD。不過個人專案不比公司產品,不會特別花錢購買完整的服務不說,自己還要學習怎麼「兜」出 CI/CD 的處理流程。市面上有不少提供 CI/CD 服務的廠商,這邊選用的是 CircleCI,免費版提供每周 2500 的額度,以及單線程處理。額度的部分老實說我不是很懂他怎麼計算的,至少我目前還沒有遇到超額得情況?單線程處理對於個人專案來說絕對夠用,畢竟只有一個人開發嘛...(汗)
CircleCI 帳號的建立以及與 GitHub 的串接,這部分網路上已有不少前輩分享請自行 Google;這邊我會以個人專案 twitter-test 為例,直接說明 config.yml 的設定配置。以下是 CI/CD 大致的流程:
- 把程式碼 merge 到 GitHub 的 master branch,觸發 CircleCI
- 開始 build
- 透過 docker 建立環境,使用 CircleCI 專用的 .env 以及測試設定,進行單元測試與整合測試
- 開始 deploy
- 利用在 CircleCI 設定的 SSH Keys,以 ssh 的方式登入 AWS EC2 更新程式碼並執行 deploy.sh
- 完成整個 CI/CD 流程
其中 5. 需要在 CircleCI 設定 SSH Keys,設定流程如下:
- 先進入 AWS EC2 主機後,設定一組 CircleCI 用的登入 key
ssh-keygen -t rsa -b 4096 -m pem -C "circleci@twitter-test"
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ubuntu/.ssh/id_rsa): /home/ubuntu/.ssh/id_rsa_circleci
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ubuntu/.ssh/id_rsa_circleci
Your public key has been saved in /home/ubuntu/.ssh/id_rsa_circleci.pub
The key fingerprint is:
SHA256:HIbCXi1JQmCDNB4EX/L3laqG7VHc8G3z/8mALUcDi6Y circleci@twitter-test
The key's randomart image is:
+---[RSA 4096]----+
|=*=o+ . |
|ooo* o + . |
| .. + * = o. |
| . + * B..o |
| . Soo.+o |
| o oo .+o. |
| . =E o +. |
| o . o o..|
| . o+|
+----[SHA256]-----+
- 這樣會產生 id_rsa_circleci 與 id_rsa_circleci.pub 2 個檔案,把 id_rsa_circleci.pub 寫入 authorized_keys 中
cat ~/.ssh/id_rsa_circleci.pub >> ~/.ssh/authorized_keys
- 取得 id_rsa_circleci 的 PRIVATE KEY
cat ~/.ssh/id_rsa_circleci
- 接著回到 CircleCI,到「Project Settings」
- 選擇左側「SSH Keys」,下面 Additional SSH Keys 區塊,選擇「Add SSH Key」
- 這邊的 Hostname 就是 AWS EC2 的對外 domain 或是 IP,Private Key 就是剛剛在 id_rsa_circleci 取得的那一長串
- 然後到左側「Environment Variables」,「Add Variable」:
- HOST_NAME:同上,AWS EC2 的對外 domain 或是 IP
- USER_NAME:登入 AWS EC2 的帳號,我這邊是使用 ubuntu
到這邊 CircleCI 與 AWS EC2 的設定就完成了,接下來要搭配 config.yml 的設定,說明如下:
version: 2
jobs:
build:
# 測試環境是根據我的 docker 檔案設定
docker:
- image: circleci/php:7.3-node-browsers
- image: mysql:5.7
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --innodb-use-native-aio=0 --server-id=1 --log_bin=ON
# 這是測試環境的 MySQL 設定,對應到 .env.circleci 裡面的 DB 設定
environment:
MYSQL_HOST: 127.0.0.1
MYSQL_DB: circleci
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: root
working_directory: ~/app
steps:
- checkout
- run: sudo apt update
- run: sudo apt install -y libsqlite3-dev zlib1g-dev
- run: sudo apt install -y mariadb-client
- run: sudo docker-php-ext-install zip
- run: sudo docker-php-ext-install pdo_mysql bcmath pcntl mbstring
- run: sudo composer self-update
- restore_cache:
keys:
- composer-v1-{{ checksum "composer.lock" }}
- composer-v1-
# 測試環境是根據 .env.circleci 的設定進行
- run: cp .env.circleci .env
- run: composer install -n --ignore-platform-reqs
- save_cache:
key: composer-v1-{{ checksum "composer.lock" }}
paths:
- vendor
# 這裡是 node.js 的設定,因為這個專案是純後端,所以都註解掉了
#- restore_cache: # special step to restore the dependency cache if `package.json` does not change
# keys:
# - node-v1-{{ checksum "package.json" }}
# # fallback to using the latest cache if no exact match is found (See https://circleci.com/docs/2.0/caching/)
# - node-v1-
#- run: npm install
#- save_cache: # special step to save the dependency cache with the `package.json` cache key template
# key: node-v1-{{ checksum "package.json" }}
# paths:
# - node_modules
#- run: npm run production
# 建立資料庫,對應到 .env.circleci 裡面的 DB 設定
- run:
name: Mysql database
command: mysql -h 127.0.0.1 -uroot -proot -e "CREATE DATABASE circleci;"
- run: php artisan key:generate
- run: php artisan migrate --env=circleci
- run: php artisan horizon:install
- run: php artisan passport:install
- run:
name: Run Laravel Server
command: php artisan serve
background: true
# 這邊執行測試時是使用 phpunit.xml.circleci 的設定
- run: vendor/bin/phpunit -c phpunit.xml.circleci
# 這邊是 Laravel Dusk 的測試,因為這個專案是純後端,所以都註解掉了
# - run:
# name: Start Chrome Driver
# command: ./vendor/laravel/dusk/bin/chromedriver-linux
# background: true
# - run:
# name: Run Laravel Dusk Tests
# command: php artisan dusk
deploy:
machine:
image: circleci/classic:edge
steps:
# 這邊其實很單純的就是 ssh 到 EC2 後,切換目錄,執行 git pull 以及 Shell Script;USER_NAME 與 HOST_NAME 則是對應到我們之前在 CircleCI 設定的環境變數
- run: ssh ${USER_NAME}@${HOST_NAME} 'cd /var/work/twitter-test/ && git pull origin master && sh ./.circleci/deploy.sh'
workflows:
version: 2
main:
jobs:
- build:
filters:
branches:
# 限定在 master 變動時,才會 build
only: master
- deploy:
requires:
- build
filters:
branches:
# 限定在 master 變動時,才會 deploy
only: master
ssh 進入主機後,因為 Laravel 有不少指令需要執行,我這邊是使用 Shell Script 幫我完成,另外因為我在 EC2 上面跑得是 docker,所以需要透過 docker exec 協助執行;以下是 deploy.sh 的說明:
#!/bin/sh
# container name
container=twitter-test
docker_command="docker exec -i $container"
# shutdown the laravel app
$docker_command php artisan down
# update PHP dependencies
$docker_command composer install --no-interaction --no-dev --prefer-dist
# --no-interaction Do not ask any interactive question
# --no-dev Disables installation of require-dev packages.
# --prefer-dist Forces installation from package dist even for dev versions.
# update database
$docker_command php artisan migrate --force
# --force Required to run when in production.
# cache boost configuration and routes
$docker_command php artisan cache:clear
$docker_command php artisan config:cache
$docker_command php artisan route:cache
# horizon
$docker_command php artisan horizon:purge
$docker_command php artisan horizon:terminate
$docker_command php artisan queue:restart
# rise from the ashes
$docker_command php artisan up
echo 'Deploy finished.'
全部執行過一遍,大概會在 CircleCI 顯示以下畫面
這些就是最基本的 CI/CD 設定了,雖然很陽春,最後 deploy 是靠 Script 執行,不是正統的分目錄版本有問題還可以緊急切換的那種...(汗)不過對於個人專案來說可以做到自動化處理,也算是有達到目的...吧?僅供參考囉
參考資料
留言列表