今天在開始之前,要先說到了開發上最容易遇到的問題,就是你的 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 時間,重新掌握開發的步調!