php| php 微服務之旅: devops

date: 2019-05-22 20:37:57
title: php| php 微服務之旅: devops

本篇屬於 PHP 微服務之旅 系列, 此係列會持續更新, 敬請期待.

如果說 微服務應用 對比 傳統巨石應用 最大的難點在於 基礎設施 的複雜度上, 那麼本篇就是要拋磚引玉, 提供一個完整的 devops 實例, 證明一個簡單的事實:

使用 devops 後, 微服務的開發也能有 傳統巨石應用 的簡單高效.

先提前預告一下效果, 對於一個參與到項目的開發而言:

  • 本地只需要安裝 git/docker, 容器啓動後, 項目所需的開發環境全都有
  • 開發者除了正常編寫代碼, 項目的 CI/CD 會在代碼提交後自動觸發, 開發者完全不用分心

devops 流程一覽

  • github 私有庫 / gitlab 私有部署
  • 綁定 Travis CI 持續集成
  • 綁定 aliyun 容器鏡像服務(cr), 設置自動構建規則
  • 綁定 aliyun 容器服務swarm(cs), 設置鏡像構建後自動部署
  • 綁定 aliyun 日誌服務(sls), 設置容器日誌自動投遞到日誌服務

下面詳細講解下各部分

git

類似對比 git 與 svn 這種事, 不會再出現在我的 blog 中, 有種去翻歷史塵埃的感覺, 大概比較適合 技術史 一類的文章.

記一些 git 常用的功能吧:

  • git add/commit/stash/push 這些基礎命令不熟悉的話, 估計對應的 工作去/暫存區/提交區/遠程分支 這些基礎概念也不熟, 這是基礎, 自行百度補
  • git branch/checkout/merge 這些分支相關命令, 也要很熟悉
  • git tag 只是在 commit 上做了一個標記, 建議使用統一的格式
  • git clone 有 2 種方式, ssh 需要配置公鑰, https 需要賬號密碼認證, 可以使用 git config --global credential.helper "store" 記住賬號密碼
  • git 是分佈式的, 本地就是一個完整的倉庫(repository), 通常會添加一個遠程倉庫來完成大家的協作
    • 通常這個遠程地址命名爲 origin, 可以通過 git remote add origin url-xxx 來添加
    • 當然遠程分支是可以修改的 git remote set-url origin url-xxx
    • 當然遠程分支可以有多個
      • 比如 github 提 PR, 需要 fork 原項目, fork 的出來的項目除了有遠程分支 origin 外, 還會多一個名爲 upstream 的遠程分支對應原項目
      • 當然也可以添加更多, 比如 git remote set-url gitee url-xxx, 添加一個國內的 git 庫, 速度更快
  • 推薦使用的 git 客戶端: github desktop
    • 命令行纔是最好用的, 但是有一個場景除外, git diff, 強調一點, 自己寫的代碼, 自己一定要 review
    • github 自家的, 所以提 PR 使用快捷鍵 cmd-r 就可以了, 賊爽, 推薦給堅持開源的小夥伴
  • 開發過程中通常使用 MR(merge request), 儘量做好 自測/code-review/CI, 從而儘量不發生 revert, 避免不了也不要慌, gitlab revert MR 體驗賊好
  • 添加 alias, 爲 git 命令行操作加速

git alias 推薦:

alias gc='git checkout'
alias gb='git branch'
alias gt='git tag'
alias gs='git status'
alias gd='git diff'
alias gl="git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
alias ga='git add -A'
alias gm='git merge'
alias gp='git pull'
alias gcl='git clone --progress'
# alias gp='git pull --rebase'
alias gpp='git pull;git push'
alias gco='git commit -am'
alias gca='git commit -am "update";git pull;git push'

# 打 tag 也可以很輕鬆
function gtt
    set tag_name release-v(date +"%Y%m%d%H%M")
    echo $tag_name | pbcopy
    pwd
    git pull
    git push
    git tag $tag_name
    git push --tags
end

# 發版到測試環境
function gdt
    set b_ori (git symbolic-ref --short -q HEAD)
    git checkout dev
    git merge $b_ori -m 'merge'
    git pull
    git push
    git push gh dev
    git checkout -
end

我使用的 fishshell, 再次瘋狂安利一波, 幾乎零配置, 比 zsh 更高效.

可供參考的教程:

git 的部分稍顯囉嗦, 後面的 docker 部分也會有點, 畢竟 git/docker 都是 基礎功, 這是沒法迴避的部分, 後面的實施部分要簡單不少, 幾張截圖就能搞定

docker

一樣的風格, 不再贅述 docker 好在哪, 以及 docker 的一些基礎概念, 默認你已經瞭解以下基礎知識:

  • docker 的基本概念: 鏡像 容器 網絡(網絡基礎/端口映射) 文件掛載 ...
  • docker Dockerfile docker-compose 的基礎知識和基本用法

當然, 如果團隊有 老司機, 你會發現使用 docker 是一件如此輕鬆的事, 幾乎只是幾個命令的事兒.

假設 git 倉庫中提供了 docker-compose.yml 文件:

version: '3.1'
services:
    s1: # 定義服務名稱
        image: registry.cn-shanghai.aliyuncs.com/daydaygo/dev:hyperf
        volumes: # 掛載代碼路徑到容器中
            - ../:/data
        ports: # 端口綁定, 如果需要本地個可以訪問容器中的服務
            - "80:9501"
        environment: # 設置環境變量
            APP_ENV: dev
        links: # 此服務依賴的其他服務, 按需配置
            - redis
            - mysql
            - rabbitmq       
        tty: true # 設置允許進入容器 tty

那麼, 其他開發者日常只需要:

# 定義 alias, 後面省力不少
alias doc='docker-compose

doc up -d s1 # 啓動服務(就是 docker 容器), 即由上面的 docker-compose.yml 文件定義的服務
dos stop/restart/rm s1 # 服務管理
doc exec s1 fish # 進入到容器tty, 使用 fishshell(是的, 再次安利 fishshell)

看到這裏, 希望你注意到一個更加吃驚的事實:

本地除了 git/docker, 其他什麼環境都不需要配置, 容器啓動起來後, 項目所需的環境都有了!!!

綁定 aliyun 容器鏡像服務(cr)

從這一部分開始, 後面的內容都會比較輕鬆, 幾乎只是幾張截圖.

  • 使用 aliyun 容器鏡像服務, 劃重點: 免費.
  • 綁定代碼源, 推薦 github/gitlab:
  • 配置自動構建規則

除了默認的 tag 觸發的自動構建(這也是上面配置 git tag alias 的原因之一), 我通常會配置 master 分支對應鏡像 tag latest, dev 分支對應鏡像 tag test, 分別部署到 測試環境/線上環境. 當然, 需要增加其他環境, 再增加一個構建規則就好了.

而我們所需要的代碼, 即 Dockerfile, 簡單得不好意思貼進來:

  • 項目鏡像構建所需的 Dockerfile
# 獲取基礎鏡像
FROM registry.cn-shanghai.aliyuncs.com/daydaygo/dev:hyperf
LABEL maintainer="daydaygo <[email protected]>" version="1.0"

# 添加項目源碼
COPY . /data
# 設置環境相關配置
COPY .env.prod /data/.env
# 啓動服務
ENTRYPOINT ["php", "/data/bin/hyperf.php", "start"]

綁定 aliyun 容器服務swarm(cs)

容器服務其實也是免費的, 收費的部分其實是集羣對應的 ecs. 這裏囉嗦一些 集羣 的概念

  • 集羣的邏輯概念: 一個集羣中可以有多個應用, 每個應用可能由多個服務協同, 比如上面的 docker-compose.yml 中的 s1 服務, 使用 --links 依賴了 mysql/redis/rabbitmq 3 個服務
  • 集羣的物理概念: 一個集羣中可以有多臺 ecs, 提供相應的 cpu/內存 等計算資源, 這些 ecs 被稱之爲 節點
  • 集羣其他資源: 網絡 文件掛載 配置項 ...

使用也很簡單, 新建一個集羣, 配置好 ecs, 開發測試的話推薦使用 按量付費, 最好至少開 2 臺來體驗.

  • 好了, 配置我們的第一個應用
  • 使用配置文件創建
hyperf-ms: # 定義服務名稱
  # 使用容器鏡像服務中的鏡像
  image: 'registry-vpc.cn-shanghai.aliyuncs.com/daydaygo/hyperf-ms:latest'
  mem_limit: 0
  kernel_memory: 0
  memswap_reservation: 0
  # 容器自動重啓
  restart: always
  shm_size: 0
  expose:
    - 9501
  links:
    - rabbitmq
  memswap_limit: 0
  # aliyun 容器服務提供的特殊功能標籤
  labels:
    # routing: 簡單 http/https 路由, 配置服務的 端口號->域名
    aliyun.routing.port_9501: ms.daydaygo.top
    # scale: 應用數量
    aliyun.scale: '1'
    # log: 關聯 aliyun 日誌服務, 這裏投遞容器的 stdout 到日誌服務中
    aliyun.log_store_stdout: stdout
    aliyun.log_ttl_stdout: 30
    aliyun.log.timestamp: false
    aliyun.depends: rabbitmq
rabbitmq:
    image: rabbitmq:3.7.8-management-alpine
    hostname: myrabbitmq
    ports:
        - "5672:5672" # mq
        - "15672:15672" # admi
  • 配置觸發器, 鏡像更新後自動部署
  • 也可以在容器鏡像服務中配置

關聯 aliyun 日誌服務

關於日誌服務, 我之前也囉嗦了不少, 有興趣可以去看看之前的 blog:

上面配置好 容器服務投遞日誌到 日誌服務 後, 接下來我們還有幾步工作要做:

  • 自動創建好的 日誌 project, 先修改一下備註
  • 對應的 logstore
  • 修改 logtail 配置, 推薦使用 json 解析, 幾乎零配置
  • json 解析, 需要日誌輸出爲 json 格式, 這個也簡單, 以 PHPer 使用的 monolog 爲例, 只用配置一下 Formatter 就好了:
$app_env = env('APP_ENV', 'dev');
if ($app_env == 'dev') {
    $formatter = [
        'class' => \Monolog\Formatter\LineFormatter::class,
        'constructor' => [
            'format' => "||%datetime%||%channel%||%level_name%||%message%||%context%||%extra%\n",
            'allowInlineLineBreaks' => true,
            'includeStacktraces' => true,
        ],
    ];
} else {
    $formatter = [
        // 看這裏
        'class' => \Monolog\Formatter\JsonFormatter::class,
        'constructor' => [],
    ];
}

return [
    'default' => [
        'handler' => [
            'class' => \Monolog\Handler\StreamHandler::class,
            'constructor' => [
                'stream' => 'php://stdout',
                'level' => \Monolog\Logger::INFO,
            ],
        ],
        'formatter' => $formatter,
    ],
];
  • 配置好索引
  • 日誌服務更多配置
    • 保存快查
    • 配置儀表盤 訂閱儀表盤的日報
    • 配置告警 推薦告警到釘釘

一言以蔽之: 日誌服務真的是個寶藏男孩

綁定 Travis CI

CI 的根本在於: 你得有自動化測試才行

PHPer 推薦使用 phpunit, 尤其是微服務化了以後, 服務通常以接口的形式提供, 單測簡直不要太好用

說回正題, 來看 Travis CI, 項目中添加一個 .travis.yml 的事兒:

branches:
  only:
  - master
  - dev

language: php

services:
  - docker 

before_install:
  - docker login registry.cn-shanghai.aliyuncs.com -u xxx -p xxx
  - docker build -t ci .
  - docker run -d --name ci ci
  - sleep 5

script:
  - docker exec ci /bin/sh -c "cd /data; phpunit"

notifications:
  email:
    - [email protected]
  webhooks:
    - https://oapi.dingtalk.com/robot/send?access_token=xxx

看配置項估計也能看明白:

  • 配置分支觸發
  • 根據項目下的 Dockerfile 構建鏡像
  • 使用鏡像運行容器
  • 執行容器中的 phpunit
  • 通過 email/釘釘 通知 CI 構建信息

寫在最後

內容看起來很多, 對參與開發的人而言, 其實很省心:

本地除了安裝 git/docker 外, 並不需要其他環境; 除了寫代碼外, 不需要分心 CI/CD

最後再安利一下使用到的一些工具:

  • git / github / github desktop / gitlab
  • docker / docker-compose
  • fishshell
  • aliyun: 容器鏡像服務(cr) 容器服務swarm(cs) 日誌服務(sls)
  • 釘釘: 使用釘釘機器人來蒐集消息, 當做消息box使用, 上面的所有流程, 都可以配置消息到釘釘
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章