Hôm nay chúng ta sẽ tìm hiểu các số khái niệm cần thiết khi deploy dựa án cakephp sử dụng circleCI aws elastic beastalk. Cùng xem file config circleCI sau và giải thích các config khi deploy dự án.
version: 2.1
orbs:
aws-cli: circleci/aws-cli@4.1.3
aws-ecr: circleci/aws-ecr@9.1.0
eb: circleci/aws-elastic-beanstalk@2.0.1
node: circleci/node@5.2.0
php: circleci/php@1.1.0
commands:
setup-aws-credentials-dev:
steps:
- aws-cli/setup:
aws_access_key_id: $AWS_ACCESS_KEY_ID
aws_secret_access_key: $AWS_SECRET_ACCESS_KEY
region: ap-southeast-1
- run:
name: AWS credentials export environment variables
command: |
echo 'export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID' >> $BASH_ENV
echo 'export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY' >> $BASH_ENV
echo 'export AWS_DEFAULT_REGION=ap-southeast-1' >> $BASH_ENV
setup-aws-credentials-prod:
steps:
- aws-cli/setup:
aws_access_key_id: $PROD_AWS_ACCESS_KEY_ID
aws_secret_access_key: $PROD_AWS_SECRET_ACCESS_KEY
region: ap-southeast-1
- run:
name: AWS credentials export environment variables
command: |
echo 'export AWS_ACCESS_KEY_ID=$PROD_AWS_ACCESS_KEY_ID' >> $BASH_ENV
echo 'export AWS_SECRET_ACCESS_KEY=$PROD_AWS_SECRET_ACCESS_KEY' >> $BASH_ENV
echo 'export AWS_DEFAULT_REGION=ap-southeast-1' >> $BASH_ENV
copy-cake-env-file:
parameters:
env:
type: string
steps:
- run:
name: Set .env from file << parameters.env >>
working_directory: ./app/config
command: |
cp -f .env.<< parameters.env >> .env
if [ << parameters.env >> == "circleci" ]
then
source .env
else
echo .env.<< parameters.env >>
fi
ls -all
check-build-status:
steps:
- run:
name: check build status of master branch
command: |
api_url=https://circleci.com/api/v1.1/project/github/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/tree/master
while
result=$(curl -H "Circle-Token: ${CIRCLE_TOKEN}" $api_url | jq -r 'unique_by(.workflows.job_name) | .[] | [.workflows.job_name, .lifecycle, .outcome] | @tsv')
echo "$result"
# 1 個以上の全てが finished になるまで
echo "$result" | fgrep -q -v finished
do
sleep 60
done
if echo "$result" | fgrep -q failed; then exit 1; fi
build-and-push-docker-image:
parameters:
region:
type: string
default: ap-southeast-1
repo:
type: string
default: coincome
tag:
type: string
default: latest
account_id:
type: string
auth:
type: steps
default: []
steps:
- aws-ecr/build_and_push_image:
auth: << parameters.auth >>
account_id: << parameters.account_id >>
build_path: ./docker/phpfpm
path: ./docker/phpfpm
extra_build_args: "--target prod"
repo: << parameters.repo >>
tag: << parameters.tag >>
region: << parameters.region >>
- steps: << parameters.auth >>
- run:
name: Remove untaged images
command: |
# tag を push すると、すでに存在する tag が untagged になり、ECRに残り続けるので削除する
IMAGE_IDS=$(aws ecr list-images --region << parameters.region >> --repository-name << parameters.repo >> --filter "tagStatus=UNTAGGED" --query 'imageIds[*]' --output json --max-items 100)
aws ecr batch-delete-image --region << parameters.region >> --repository-name << parameters.repo >> --image-ids "$IMAGE_IDS" || true
# - run:
# name: Test image
# working_directory: ~/project/docker
# command: |
# docker run [ecr_image] php -v
# echo
# docker run [ecr_image] supervisord -v
jobs:
build-and-push-docker-image-dev:
parameters:
tag:
type: string
executor:
name: aws-ecr/default
steps:
- build-and-push-docker-image:
region: "ap-southeast-1"
repo: "coincome"
tag: << parameters.tag >>
account_id: "211699566303"
auth:
- setup-aws-credentials-dev
build-webpack:
executor:
name: node/default
tag: "12.19"
steps:
- checkout
- node/install-packages:
cache-path: ~/project/node_modules
override-ci-command: npm install
- run:
name: Build webpack
command: npm run dev
- persist_to_workspace:
root: ~/project
paths:
- app/webroot
check-build-status-master:
docker:
- image: cimg/base:stable
steps:
- check-build-status
build-php:
docker:
- image: cimg/php:8.2
steps:
- checkout
- php/install-composer
- php/install-packages:
app-dir: app
with-cache: true
- persist_to_workspace:
root: ~/project
paths:
- app/vendor
build-php-subfolder:
docker:
- image: cimg/php:8.2-node
steps:
- checkout
- php/install-composer
- run:
name: Pre installe required tools
command: |
sudo apt-get update -y
sudo apt-get install -y libicu-dev libmemcached-dev
echo "/usr" | sudo pecl install memcached
echo "extension=memcached.so" | sudo tee /usr/local/etc/php/conf.d/memcached.ini
sudo docker-php-ext-install >/dev/null -j$(nproc) gettext
- php/install-packages:
app-dir: app2
with-cache: true
- persist_to_workspace:
root: ~/project
paths:
- app2/vendor
test:
docker:
- image: cimg/php:8.2
- image: cimg/mysql:8.0
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: "test_circleci_db"
MYSQL_USER: ubuntu
MYSQL_PASSWORD: password
command: ["--sql-mode="]
resource_class: medium+
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: true
- attach_workspace:
at: ~/project
- run:
name: Waiting for MySQL to be ready
command: dockerize -wait tcp://127.0.0.1:3306 -timeout 30s
- copy-cake-env-file:
env: circleci
- run:
name: Make sure folder vender and webroot existed
command: |
cp -r ./app/config/app.default.php ./app/config/app.php
- run:
name: Migration data for test
working_directory: app
command: |
bin/cake migrations migrate
bin/cake migrations migrate -p LoginAttempts
bin/cake migrations migrate -p Queue
php bin/cake.php schema_cache clear
- run:
working_directory: app
name: Run tests
command: composer test
- run:
name: composer po-check
command: composer po-check -- --ansi
working_directory: app
deploy:
executor:
name: aws-cli/default
parameters:
aws-account-id:
type: string
environment-name:
type: string
application-name:
type: string
region:
type: string
default: ap-southeast-1
repo:
type: string
is-web-server:
type: boolean
tag:
type: string
default: latest
app-env:
type: string
auth:
type: steps
default: []
steps:
- checkout
- attach_workspace:
at: ~/project
- steps: << parameters.auth >>
- run:
name: Update to application name "<< parameters.application-name >>" .elasticbeanstalk/config.global.yml
command: |
cp -af .elasticbeanstalk/config.global.org.yml .elasticbeanstalk/config.global.yml
sed -i -e 's/APPLICATIONNAME/<< parameters.application-name >>/' .elasticbeanstalk/config.global.yml
sed -i -e 's/APPLICATION_REGION/<< parameters.region >>/' .elasticbeanstalk/config.global.yml
cat .elasticbeanstalk/config.global.yml
- run:
name: Update << parameters.application-name >> for 02.migration.config
command: |
env="<< parameters.environment-name >>"
if [[ $env == *"worker"* ]]; then
cp -af deploy/Dockerrun.worker.json Dockerrun.aws.json
elif [[ $env == *"prod"* ]]; then
cp -af deploy/Dockerrun.prod.json Dockerrun.aws.json
else
cp -af deploy/Dockerrun.dev.json Dockerrun.aws.json
fi
sed -i -e 's/DOCKER_IMAGE_NAME/<< parameters.repo >>/' .platform/hooks/postdeploy/02_migrations.sh
sed -i -e 's/DOCKER_IMAGE_VERSION/<< parameters.tag >>/' .platform/hooks/postdeploy/02_migrations.sh
sed -i -e 's/AWS_ACCOUNT_ID/<< parameters.aws-account-id >>/' .platform/hooks/postdeploy/02_migrations.sh
sed -i -e 's/AWS_DEFAULT_REGION/<< parameters.region >>/' .platform/hooks/postdeploy/02_migrations.sh
chmod +x .platform/hooks/postdeploy/02_migrations.sh
sed -i -e 's/DOCKER_IMAGE_NAME/<< parameters.repo >>/' .ebextensions/04.crontab.config
sed -i -e 's/DOCKER_IMAGE_VERSION/<< parameters.tag >>/' .ebextensions/04.crontab.config
sed -i -e 's/AWS_ACCOUNT_ID/<< parameters.aws-account-id >>/' .ebextensions/04.crontab.config
sed -i -e 's/AWS_DEFAULT_REGION/<< parameters.region >>/' .ebextensions/04.crontab.config
sed -i -e 's/DOCKER_IMAGE_NAME/<< parameters.repo >>/' Dockerrun.aws.json
sed -i -e 's/DOCKER_IMAGE_VERSION/<< parameters.tag >>/' Dockerrun.aws.json
sed -i -e 's/AWS_ACCOUNT_ID/<< parameters.aws-account-id >>/' Dockerrun.aws.json
sed -i -e 's/AWS_DEFAULT_REGION/<< parameters.region >>/' Dockerrun.aws.json
- run:
name: Init ECS task (Dockerrun.aws.json) update memory
command: |
# Get EB instance type
instanceType=$(aws ec2 describe-instances --filters "Name=tag:elasticbeanstalk:environment-name,Values=<< parameters.environment-name >>" --query "Reservations[0].Instances[0].InstanceType" --output text)
# Get memory info of instance type
instanceMemory=$(curl -s https://tyrell-aws.s3-ap-northeast-1.amazonaws.com/ec2.json | jq --arg instanceType $instanceType '.[] | select (.InstanceType==$instanceType) | .Memory | tonumber')
if [ -z "$instanceMemory" ]; then
# exit if memory not found
exit 1
fi
# GiB * 1024 = MiB , assign 70% memory
MEMORY=$(echo $instanceMemory | awk '{printf ("%.0f", $1 * 1024 * .7)}')
cat Dockerrun.aws.json \
| jq --arg memory $MEMORY \
'(.containerDefinitions[] | select(.name == "php-app" or .name == "app-worker") | .memory) |= ($memory | tonumber)' \
> Dockerrun.aws.json.new && \
mv -f Dockerrun.aws.json.new Dockerrun.aws.json
- copy-cake-env-file:
env: << parameters.app-env >>
- run:
name: Show Dockerrun.aws.json
command: |
cat Dockerrun.aws.json
- run:
name: Make sure folder vender and webroot existed
command: |
cp -r ~/project/app/config/app.default.php ~/project/app/config/app.php
- run:
name: Create assetlinks files
command: |
env="<< parameters.environment-name >>"
if [[ $env == *"dev"* ]]; then
cp -af app/webroot/.well-known/assetlinks.json.dev app/webroot/.well-known/assetlinks.json
cp -af app/webroot/apple-app-site-association.dev app/webroot/apple-app-site-association
elif [[ $env == *"stg"* ]]; then
cp -af app/webroot/.well-known/assetlinks.json.stg app/webroot/.well-known/assetlinks.json
cp -af app/webroot/apple-app-site-association.stg app/webroot/apple-app-site-association
else
cp -af app/webroot/.well-known/assetlinks.json.prod app/webroot/.well-known/assetlinks.json
cp -af app/webroot/apple-app-site-association.prod app/webroot/apple-app-site-association
fi
- unless:
condition: <>
steps:
- run: rm .ebextensions/00.network-load-balancer.config
- run:
name: Remove migrations script if not worker environment
command: |
env="<< parameters.environment-name >>"
if [[ $env == *"worker"* ]]; then
echo "OK"
else
rm -f .platform/hooks/postdeploy/02_migrations.sh
fi
- eb/setup
- run:
name: Deploy with elastic beanstalk << parameters.environment-name >>, estimate time 5-10 minutes.
command: eb deploy --label "<< parameters.environment-name >>-<< pipeline.number >>" "<< parameters.environment-name >>" --debug
filters-branch-develop: &filters-branch-develop
branches:
only:
- develop
filters-branch-staging: &filters-branch-staging
branches:
only:
- master
filter_tags_release: &filter_tags_release
tags:
only: /^v[0-9]+(\.[0-9]+)$/
branches:
ignore: /.*/
filter_branch_other: &filter_branch_other
tags:
ignore: /^v[0-9]+(\.[0-9]+)$/
branches:
ignore:
- master
- develop
workflows:
version: 2
test:
jobs:
- build-webpack:
filters: *filter_branch_other
- build-php:
filters: *filter_branch_other
- test:
filters: *filter_branch_other
requires:
- build-php
deploy-with-build-and-test-develop:
jobs:
- build-webpack:
filters: *filters-branch-develop
- build-php:
filters: *filters-branch-develop
- test:
filters: *filters-branch-develop
requires:
- build-php
- build-and-push-docker-image-dev:
tag: develop
requires:
- build-webpack
- build-php
filters: *filters-branch-develop
- deploy:
auth:
- setup-aws-credentials-dev
name: deploy_develop_web
is-web-server: true
tag: "develop"
environment-name: "demo-dev-al2023"
app-env: "dev"
aws-account-id: [your_account_id]
repo: "app"
application-name: "app-demo"
requires:
- build-and-push-docker-image-dev
filters: *filters-branch-develop
- deploy:
auth:
- setup-aws-credentials-dev
name: deploy_develop_worker
is-web-server: false
tag: "develop"
environment-name: "demo-al2023-worker"
app-env: "worker-dev"
aws-account-id: [your_account_id]
repo: "app"
application-name: "app-demo"
requires:
- build-and-push-docker-image-dev
filters: *filters-branch-develop
deploy-with-build-and-test-staging:
jobs:
- build-webpack:
filters: *filters-branch-staging
- build-php:
filters: *filters-branch-staging
- test:
filters: *filters-branch-staging
requires:
- build-php
- build-and-push-docker-image-dev:
tag: latest
requires:
- build-webpack
- test
filters: *filters-branch-staging
- deploy:
auth:
- setup-aws-credentials-dev
name: deploy_staging_web
is-web-server: true
tag: "develop"
environment-name: "demo-stg-al2023"
app-env: "stg"
aws-account-id: [your_account_id]
repo: "app"
application-name: "app-demo"
requires:
- deploy_staging_worker
filters: *filters-branch-staging
- deploy:
auth:
- setup-aws-credentials-dev
name: deploy_staging_worker
is-web-server: false
tag: "develop"
environment-name: "demo-stg-al2023-worker"
app-env: "worker-stg"
aws-account-id: [your_account_id]
repo: "app"
application-name: "app-demo"
requires:
- build-and-push-docker-image-dev
filters: *filters-branch-staging
deploy-prod:
jobs:
- build-webpack:
filters: *filter_tags_release
- build-php:
filters: *filter_tags_release
- check-build-status-master:
filters: *filter_tags_release
- deploy:
auth:
- setup-aws-credentials-prod
name: deploy_prod_web
is-web-server: true
tag: latest
environment-name: "demo-prod-al2023"
app-env: "prod"
aws-account-id: [your_account_id]
repo: "app"
application-name: "app-demo"
requires:
- deploy_prod_worker
filters: *filter_tags_release
- deploy:
auth:
- setup-aws-credentials-prod
name: deploy_prod_worker
is-web-server: false
tag: latest
environment-name: "demo-prod-al2023-worker"
app-env: "worker-prod"
aws-account-id: [your_account_id]
repo: "app"
application-name: "app-demo"
requires:
- build-webpack
- build-php
- check-build-status-master
filters: *filter_tags_release
Định nghĩa các công việc chính cần làm, như test một nhánh, deploy code lên môi trường dev, stg, prod. Trong workflow sẽ đinh nghĩa các công việc cần làm(jobs), định nghĩa jobs chạy song song hay tuần tự, và điều kiện khi nào workflow hoạt động. VD: Workflows test sẽ chạy 2 jobs build-webpack và build-php song song, khi build xong job build-php mới thực hiện job test, điểu kiện để chạy workflow test là các nhánh không phải develop, staging và tag release.
Định nghĩa các bước làm 1 công việc cụ thể, đinh nghĩa môi trường thực thi, sử dụng các orbs được viết sẵn, định nghĩa các tham số cho command. VD: job build-php Định nghĩa các công việc để build một môi trường php 8.2, các bước build môi trường php 8.2 checkout code, cài đặt composer, cài đặt các package với composer.
Có thể hiểu như một function được sửa dụng trong jobs, 1 command bao gồm các step và có thể truyền được tham số vào từ jobs. Viết command giúp định nghĩa job không bị lặp code.
Orbs là các packages hay được sửa dụng, dùng job sẽ tiết kiệm thời gian định nghĩa job. Do các công việc hay làm đã được đóng gọi sẵn, chỉ việc dùng orbs không cần phải viết lại code dài dòng
Dùng để định nghĩa điều kiện khi nào workflow được chạy. VD: filters-branch-develop Đinh nghĩa khi code được merge vào branch develop sẽ thoả mãn điều kiện filter.
Như vậy là chúng ta đã tìm hiểu xong các bước cơ bản để deploy code sử dụng circleCI. Thanks for reading...
https://circleci.com/docs/deploy-to-aws/ https://circleci.com/docs/concepts/ https://kevingoedecke.com/2018/03/12/circleci-2-0-beanstalk-example-tutorial/