close

CI/CD 在整個開發流程中算是最後的環節,如果公司大一些有 MIS/DevOps 部門,這一塊通常會有專業人士處理;工程師寫完程式後只要 merge master branch,理論上就會自動觸發 CI/CD。不過個人專案不比公司產品,不會特別花錢購買完整的服務不說,自己還要學習怎麼「兜」出 CI/CD 的處理流程。市面上有不少提供 CI/CD 服務的廠商,這邊選用的是 CircleCI免費版提供每周 2500 的額度,以及單線程處理。額度的部分老實說我不是很懂他怎麼計算的,至少我目前還沒有遇到超額得情況?單線程處理對於個人專案來說絕對夠用,畢竟只有一個人開發嘛...(汗)

CircleCI 帳號的建立以及與 GitHub 的串接,這部分網路上已有不少前輩分享請自行 Google;這邊我會以個人專案 twitter-test 為例,直接說明 config.yml 的設定配置。以下是 CI/CD 大致的流程:

  1. 把程式碼 merge 到 GitHub 的 master branch,觸發 CircleCI
  2. 開始 build
  3. 透過 docker 建立環境,使用 CircleCI 專用的 .env 以及測試設定,進行單元測試與整合測試
  4. 開始 deploy
  5. 利用在 CircleCI 設定的 SSH Keys,以 ssh 的方式登入 AWS EC2 更新程式碼並執行 deploy.sh
  6. 完成整個 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

20200909005.jpg

- 接著回到 CircleCI,到「Project Settings」

20200909001.jpg

- 選擇左側「SSH Keys」,下面 Additional SSH Keys 區塊,選擇「Add SSH Key」

20200909003.jpg

- 這邊的 Hostname 就是 AWS EC2 的對外 domain 或是 IP,Private Key 就是剛剛在 id_rsa_circleci 取得的那一長串

20200909004.jpg

- 然後到左側「Environment Variables」,「Add Variable」:

  • HOST_NAME:同上,AWS EC2 的對外 domain 或是 IP
  • USER_NAME:登入 AWS EC2 的帳號,我這邊是使用 ubuntu

20200909002.jpg

到這邊 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 顯示以下畫面

20200909019.jpg

這些就是最基本的 CI/CD 設定了,雖然很陽春,最後 deploy 是靠 Script 執行,不是正統的分目錄版本有問題還可以緊急切換的那種...(汗)不過對於個人專案來說可以做到自動化處理,也算是有達到目的...吧?僅供參考囉

參考資料

Laravel(EC2)CircleCIによるデプロイ(deploy)の自動化

arrow
arrow
    創作者介紹
    創作者 danielhuang030 的頭像
    danielhuang030

    danielhuang030 的研究日誌

    danielhuang030 發表在 痞客邦 留言(0) 人氣()