今天在開始之前,要先說到了開發上最容易遇到的問題,就是你的 git 的遊戲規則是  git flow 、 github flow 還是 gitlab flow?
我覺得這個問題不難,就是越簡單越好,因為 YAGNI (You aren't gonna need it)。
因為連 gitlab 也開始拋棄了固定的 gitlab flow 策略,轉向更通用且彈性的策略上。
所以在這個專案中,借鑑於 GitOps 的一些概念,在這邊所採取的策略是:
main、develop 跟 PR 產生的分支
develop: 使用上如一般的 Staging 環境一樣,是比照 production 的測試區main: production 的分支main
所以從上面的規劃來看,我們會需要有兩個線上環境,跟一個本地開發環境。
為了簡化有可能遇到的非預期的環境問題,所以採用的是盡量全部環境容器化,把環境的因素都統一。
Dockerfile.base:這邊會把 container image 分為兩個部分,base 是預先將環境的依賴先安裝好,再來針對不同環境的去調整。
FROM php:8.2-cli-buster
ENV COMPOSER_ALLOW_SUPERUSER=1
#
#-----------------------------------------------
# Basic Dependency
#-----------------------------------------------
#
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        libz-dev \
        libpq-dev \
        libjpeg-dev \
        libpng-dev \
        libfreetype6-dev \
        libssl-dev \
        libmcrypt-dev \
        libmagickwand-dev \
        libxml2-dev \
        libzip-dev \
        unzip \
        && rm -r /var/lib/apt/lists/*
#
#-----------------------------------------------
# PHP Extension
#-----------------------------------------------
#
RUN docker-php-ext-install soap exif pcntl zip pdo_pgsql bcmath sockets
RUN MAKEFLAGS="-j $(nproc)" pecl install  -o -f grpc && docker-php-ext-enable grpc
RUN usermod -u 1000 www-data && groupmod -g 1000 www-data
Dockerfile.api: 這邊僅有留下 deployment 所需的步驟
FROM asia.gcr.io/readcast-2023/api-base-image:latest
#
#-----------------------------------------------
# Final Touch
#-----------------------------------------------
#
ADD .deploy/php/app.ini /usr/local/etc/php/conf.d
RUN usermod -u 1000 www-data && groupmod -g 1000 www-data
COPY . /var/www/
WORKDIR /var/www
RUN ./vendor/bin/rr get-binary
#
#-----------------------------------------------
# Optimize for production
#-----------------------------------------------
#
RUN php artisan route:cache && php artisan config:cache
VOLUME ["/var/www"]
CMD /bin/bash -c 'php artisan octane:start --server=roadrunner --host=0.0.0.0 --port=8080 --rr-config=.rr.yaml --workers=4'
cloudbuild-baseimage.yaml:這個主要是針對 Dockerfile.base 的 pipeline 設定
steps:
  # build image
  - name: 'gcr.io/cloud-builders/docker'
    args:
      - 'build'
      - '-t'
      - '${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:$BUILD_ID'
      - '-t'
      - '${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:latest'
      - '-f'
      - "${_DOCKERFILE}"
      - '.'
  # push image
  - name: 'gcr.io/cloud-builders/docker'
    args: ["push", "${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:$BUILD_ID"]
  # push image for latest
  - name: 'gcr.io/cloud-builders/docker'
    args: ["push", "${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:latest"]
timeout: 3600s
options:
  machineType: 'E2_HIGHCPU_8'
images:
  - ${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:latest
  - ${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:$BUILD_ID
substitutions:
  _IMG_REPO: 'asia.gcr.io'
  _APP_NAME: 'api-base-image'
  _DOCKERFILE: '.deploy/Dockerfile.api'
cloudbuild-baseimage.yaml:這個主要是針對 Dockerfile.api 的 pipeline 設定,會 deploy 至 cloud run
steps:
  # get env from secret manager
  - name: 'gcr.io/cloud-builders/gcloud'
    entrypoint: 'bash'
    args:
      - '-c'
      - |
        gcloud beta secrets versions access latest --secret="${_PHP_DOT_ENV}" > ./${_ENV_FILE}
  - name: 'composer:2'
    entrypoint: 'bash'
    args:
      - '-c'
      - |
        composer install --optimize-autoloader --ignore-platform-reqs --no-scripts --no-dev
        composer dump-autoload
  # build container image
  - name: 'gcr.io/cloud-builders/docker'
    args:
      - 'build'
      - '-t'
      - '${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:$BUILD_ID'
      - '-t'
      - '${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:latest'
      - '-f'
      - "${_DOCKERFILE}"
      - '.'
  # push container image
  - name: 'gcr.io/cloud-builders/docker'
    args: ["push", "${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:$BUILD_ID"]
  # push container image
  - name: 'gcr.io/cloud-builders/docker'
    args: ["push", "${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:latest"]
  # deploy on cloud run
  - name: 'gcr.io/cloud-builders/gcloud'
    args:
      - beta
      - run
      - deploy
      - ${_APP_NAME}
      - --image
      - ${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}
      - --region
      - ${_CLOUDSDK_COMPUTE_ZONE}
      - --platform
      - managed
      - --quiet
      - --cpu=${_CPU}
      - --memory=${_MEMORY}
      - --port=8080
      - --allow-unauthenticated
      - --timeout=${_TIMEOUT_LIMIT}
      - --project=${PROJECT_ID}
      - --max-instances=${_MAX_INSTANCES}
      - --min-instances=${_MIN_INSTANCES}
      - --concurrency=${_CONCURRENCY}
      - --execution-environment=gen1
images:
  - ${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:latest
  - ${_IMG_REPO}/$PROJECT_ID/${_APP_NAME}:$BUILD_ID
substitutions:
  _IMG_REPO: 'asia.gcr.io'
  _APP_NAME: 'api-production'
  _DOCKERFILE: 'Dockerfile.api'
  _ENV_FILE: '.env'
  _PHP_DOT_ENV: 'api-production-env'
  _CLOUDSDK_COMPUTE_ZONE: 'asia-east1'
  _MIN_INSTANCES: '0'
  _MAX_INSTANCES: '2'
  _CPU: '1'
  _MEMORY: '512M'
  _CONCURRENCY: '120'
  _TIMEOUT_LIMIT: '30'
明天我們會進一步回到了本地環境的建立,會使用 docker 與 docker-compose 來建立人人都可以使用的開發環境。就算是在深山中網路不穩時,也可以愉快開發的模式與工作 style。
讓容器化的環境減少不必要的 debug 時間,重新掌握開發的步調!