当前位置:   article > 正文

Gitlab: PHP项目CI/CD实践

Gitlab: PHP项目CI/CD实践

目录

1 说明

2 CI/CD

2.1 部署方式一:增量部署

2.1.1 目标服务器准备 

2.2.2 Gitlab及Envoy脚本

2.2 部署方式二:镜像构建与部署

2.2.1 推送到私有化容器仓库

准备工作

脚本

要点

2.2.2 推送到hub.docker.com

准备工作

脚本

3 参考:


1 说明

  • 以一个laravel blog项目为例,做dev分支的CI/CD实践
  • 结合laravel envoy工具做多个远程服务器部署,分两种方式:A. 增量部署 B.镜像构建与部署

服务器

SiteServerIP站点目录
team1-prj2.dev.iahost001.dev.ia192.168.0.130/www/wwwroot/team1-prj2.dev.ia
team1-prj2.dev.iahost002.dev.ia192.168.0.131/www/wwwroot/team1-prj2.dev.ia

2 CI/CD

2.1 部署方式一:增量部署

通过Lavavel/Envoy和git拉取新版本文件进行部署

2.1.1 目标服务器准备 
  • php8.2, 安装所需扩展,务必在cli下测试是否正常, 有些被disable的functions要打开
  • composer self-update, 兼容php8.2
  • 使用Laravel/Envoy分发部署,确保Envoy.blade.php是utf8格式文件
  • 站点目录结构
    1. root@host001:/www/wwwroot/team1-prj2.dev.ia# tree -d -L 3 ./
    2. ./
    3. ├── current -> /www/wwwroot/team1-prj2.dev.ia/releases/default
    4. ├── releases
    5. │   └── default
    6. │   ├── app
    7. │   ├── bootstrap
    8. │   ├── config
    9. │   ├── database
    10. │   ├── public
    11. │   ├── resources
    12. │   ├── routes
    13. │   ├── storage -> /www/wwwroot/team1-prj2.dev.ia/storage
    14. │   ├── tests
    15. │   └── vendor
    16. └── storage
    17. ├── app
    18. │   └── public
    19. ├── framework
    20. │   ├── cache
    21. │   ├── sessions
    22. │   ├── testing
    23. │   └── views
    24. └── logs

说明:

team1-prj2.dev.ia应用目录
team1-prj2.dev.ia/releases版本发布目录,这里只设置了一个default目录,也可根据需要做日期变量发布
team1-prj2.dev.ia/current

链接到最新版本,被nginx访问的站点目录路径

current -> /www/wwwroot/team1-prj2.dev.ia/releases/default/

team1-prj2.dev.ia/storage

链接到最新版本的应用数据保存目录,如:日志,缓存等

storage -> /www/wwwroot/team1-prj2.dev.ia/storage

team1-prj2.dev.ia/.dev

.dev文件是运维人员建立的服务器定制环境文件,不进入仓库,链接到项目同名文件

.env -> /www/wwwroot/team1-prj2.dev.ia/.env*

team1-prj2.dev.ia/releases/default/.gitlab-ci.ymlgitlab 部署脚本
team1-prj2.dev.ia/releases/default/Envoy.blade.phpenvoy 部署脚本

nginx配置

  1. server
  2. {
  3. listen 80;
  4. server_name team1-prj2.dev.ia;
  5. index index.php index.html index.htm default.php default.htm default.html;
  6. root /www/wwwroot/team1-prj2.dev.ia/current/public;
  7. #CERT-APPLY-CHECK--START
  8. # 用于SSL证书申请时的文件验证相关配置 -- 请勿删除
  9. include /www/server/panel/vhost/nginx/well-known/team1-prj2.dev.ia.conf;
  10. #CERT-APPLY-CHECK--END
  11. #SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
  12. #error_page 404/404.html;
  13. #SSL-END
  14. #ERROR-PAGE-START 错误页配置,可以注释、删除或修改
  15. #error_page 404 /404.html;
  16. #error_page 502 /502.html;
  17. #ERROR-PAGE-END
  18. #PHP-INFO-START PHP引用配置,可以注释或修改
  19. include enable-php-82.conf;
  20. #PHP-INFO-END
  21. #REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
  22. include /www/server/panel/vhost/rewrite/team1-prj2.dev.ia.conf;
  23. #REWRITE-END
  24. #禁止访问的文件或目录
  25. location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md)
  26. {
  27. return 404;
  28. }
  29. #一键申请SSL证书验证目录相关设置
  30. location ~ \.well-known{
  31. allow all;
  32. }
  33. #禁止在证书验证目录放入敏感文件
  34. if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
  35. return 403;
  36. }
  37. location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
  38. {
  39. expires 30d;
  40. error_log /dev/null;
  41. access_log /dev/null;
  42. }
  43. location ~ .*\.(js|css)?$
  44. {
  45. expires 12h;
  46. error_log /dev/null;
  47. access_log /dev/null;
  48. }
  49. location / {
  50. try_files $uri $uri/ /index.php?$query_string;
  51. }
  52. access_log /www/wwwlogs/team1-prj2.dev.ia.log;
  53. error_log /www/wwwlogs/team1-prj2.dev.ia.error.log;
  54. }
2.2.2 Gitlab及Envoy脚本

.gitlab-ci.yml

  1. # default:
  2. # image: edbizarro/gitlab-ci-pipeline-php:7.4
  3. #default:
  4. # image: bennybi/php8.2
  5. # image: bennybi/php7.4
  6. stages:
  7. - test
  8. - deploy
  9. .init_ssh: &init_ssh |
  10. which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )
  11. eval $(ssh-agent -s)
  12. echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
  13. mkdir -p ~/.ssh
  14. chmod 700 ~/.ssh
  15. [[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
  16. cache:
  17. key: ${CI_COMMIT_REF_SLUG}
  18. paths:
  19. - vendor/
  20. unit_test:
  21. stage: test
  22. tags:
  23. - php
  24. script:
  25. - cp .env.test .env
  26. - composer install
  27. - composer global require "laravel/envoy"
  28. - php artisan key:generate
  29. - php artisan migrate
  30. - vendor/bin/phpunit
  31. deploy_dev:
  32. stage: deploy
  33. tags:
  34. - php
  35. environment:
  36. name: dev
  37. url: http://team1-prj2.dev.ia
  38. script:
  39. - *init_ssh
  40. - vendor/bin/envoy run deploy --branch="$CI_COMMIT_BRANCH" --commit="$CI_COMMIT_SHA"
  41. rules:
  42. - if: $CI_COMMIT_BRANCH == "dev"
  43. deploy_live:
  44. stage: deploy
  45. tags:
  46. - php
  47. script:
  48. - *init_ssh
  49. - vendor/bin/envoy run deploy --commit="$CI_COMMIT_SHA"
  50. environment:
  51. name: live
  52. url: http://team1-prj2.dev.ia
  53. when: manual
  54. rules:
  55. - if: $CI_COMMIT_BRANCH == "live"

Envoy.blade.php

  1. @servers(['local' => 'deployer@host001.dev.ia','staging' => 'deployer@host002.dev.ia'])
  2. @setup
  3. $repository = 'git@host001.dev.ia:dev1/team1-prj2.git';
  4. $releases_dir = '/www/wwwroot/team1-prj2.dev.ia/releases';
  5. $app_dir = '/www/wwwroot/team1-prj2.dev.ia';
  6. $release = 'default';
  7. $new_release_dir = $releases_dir .'/'. $release;
  8. $user = get_current_user();
  9. @endsetup
  10. @story('deploy', ['on' => ['local','staging']])
  11. sync_repository
  12. run_composer
  13. update_symlinks
  14. @endstory
  15. @task('sync_repository')
  16. echo "Current User: {{$user}}, branch:{{$branch}}, commit:{{$commit}}"
  17. if [ -d "{{$new_release_dir}}" ]; then
  18. echo 'Pulling repository'
  19. cd {{ $new_release_dir }}
  20. git checkout {{ $branch }}
  21. git fetch
  22. git reset --hard HEAD
  23. git merge origin/{{ $branch }}
  24. else
  25. echo 'Cloning repository'
  26. [ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
  27. git clone --branch {{ $branch }} --single-branch --depth 1 {{ $repository }} {{ $new_release_dir }}
  28. cd {{ $new_release_dir }}
  29. git reset --hard {{ $commit }}
  30. git config --global --add safe.directory '*'
  31. fi
  32. @endtask
  33. @task('run_composer')
  34. echo "Starting deployment ({{ $release }})"
  35. cd {{ $new_release_dir }}
  36. composer install --prefer-dist --no-scripts -q -o
  37. @endtask
  38. @task('update_symlinks')
  39. if [ ! -d "{{ $app_dir }}/current" ]; then
  40. echo "Linking storage directory"
  41. {{-- rm -rf {{ $new_release_dir }}/storage --}}
  42. mv {{ $new_release_dir }}/storage {{ $app_dir }}
  43. ln -nfs {{ $app_dir }}/storage {{ $new_release_dir }}/storage
  44. echo 'Linking .env file'
  45. ln -nfs {{ $app_dir }}/.env {{ $new_release_dir }}/.env
  46. echo 'Linking current release'
  47. ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current
  48. chmod 775 -Rf {{ $releases_dir }}
  49. fi
  50. chmod 775 -Rf {{ $new_release_dir }}/storage
  51. {{-- chown deployer:www -Rf {{ $new_release_dir }}/.git --}}
  52. @endtask
2.2 部署方式二:镜像构建与部署
2.2.1 推送到私有化容器仓库
准备工作

-  新建项目team1-prj1,初始化git 仓库为:http://host001.dev.ia:18181/dev1/team1-prj1.git

-  建好容器仓库(见前文相关部分

-  需要在Admin Area->CI/CD->Variables添加docker访问用户变量

  1. $LOCAL_REGISTRY_LOGIN = your docker username
  2. $LOCAL_REGISTRY_PASSWORD
脚本

 项目中添加Dockerfile 文件,这里用到的原型镜像是我之前定制的php8.2

  1. FROM bennybi/php8.2:latest
  2. RUN apt-get update -y && apt-get install -y openssl zip unzip git
  3. RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
  4. # RUN docker-php-ext-install pdo mbstring
  5. WORKDIR /app
  6. COPY .env.test .env
  7. COPY . /app
  8. RUN composer install
  9. # Run any additional commands specific to Laravel
  10. RUN php artisan config:cache
  11. RUN php artisan route:cache
  12. RUN php artisan view:cache
  13. CMD php artisan serve --host=0.0.0.0 --port=8181
  14. EXPOSE 8181

 .gitlab-ci.yml

  1. default:
  2. image: docker:19.03.8
  3. services:
  4. - mysql:5.7
  5. before_script:
  6. - docker info
  7. variables:
  8. # DOCKER_IMAGE_TAG: 'bennybi/team1-prj1'
  9. APP_NAME: 'team1-prj1'
  10. REGISTRY_URL: 'http://host001.dev.ia:5050'
  11. DOCKER_IMAGE_TAG: 'host001.dev.ia:5050/dev1/team1-prj1:1.0.0'
  12. # DOCKER_IMAGE_TAG: 'host001.dev.ia:5050/dev1/team1-prj1'
  13. CONTAINER_IMAGE_NAME: ${DOCKER_IMAGE_TAG}
  14. MYSQL_DATABASE: test
  15. MYSQL_ROOT_PASSWORD: fa843c707ce26702
  16. DB_HOST: host001.dev.ia
  17. DB_USERNAME: developer
  18. stages:
  19. - test
  20. - build
  21. - deploy
  22. unit_test:
  23. stage: test
  24. tags:
  25. - php
  26. script:
  27. - cp .env.test .env
  28. - composer install
  29. - php artisan key:generate
  30. - php artisan migrate
  31. - vendor/bin/phpunit
  32. build_image:
  33. stage: build
  34. tags:
  35. - php
  36. script:
  37. - docker login ${REGISTRY_URL} -u $LOCAL_REGISTRY_LOGIN -p $LOCAL_REGISTRY_PASSWORD
  38. - docker build --output type=registry,oci-mediatypes=false --cache-from "${DOCKER_IMAGE_TAG}" -t "${DOCKER_IMAGE_TAG}" --push --provenance=false .
  39. # - docker push ${DOCKER_IMAGE_TAG}:latest
  40. - docker push ${DOCKER_IMAGE_TAG}
  41. rules:
  42. - if: $CI_COMMIT_BRANCH == "dev"
  43. deploy_dev:
  44. stage: deploy
  45. tags:
  46. - php
  47. script:
  48. - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
  49. - eval $(ssh-agent -s)
  50. - ssh-add <(echo "$SSH_PRIVATE_KEY")
  51. - mkdir -p ~/.ssh
  52. - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
  53. - ssh deployer@host002.dev.ia "docker login ${REGISTRY_URL} -u $LOCAL_REGISTRY_LOGIN -p $LOCAL_REGISTRY_PASSWORD && docker pull $CONTAINER_IMAGE_NAME && docker stop ${APP_NAME} || true && docker rm ${APP_NAME} || true && docker run --name ${APP_NAME} -d -p 8181:8181 ${CONTAINER_IMAGE_NAME}"
  54. environment:
  55. name: dev
  56. url: http://team1-prj1.dev.ia
  57. # when: manual
  58. rules:
  59. - if: $CI_COMMIT_BRANCH == "dev"
要点

- 注意build时所需的参数,缺少会诱发错误: “Invalid tag: missing manifest digest”

We used the buildx flag “–output type=registry,oci-mediatypes=false” to generate a Docker v2.2 manifest list.  We could set the value to “true” and generate an OCI manifest index, but the GitLab UI will incorrectly display “Invalid tag: missing manifest digest”. 

 推送结果图示:

2.2.2 推送到hub.docker.com
准备工作

-  新建项目team1-prj1,初始化git 仓库为:http://host001.dev.ia:18181/dev1/team1-prj1.git

-  构建的镜像将push到hub.docker.com,因此需要在Admin Area->CI/CD->Variables添加docker访问用户变量

  1. $DOCKER_LOGIN = your docker username
  2. $DOCKER_PASSWORD
脚本

Dockerfile 

同上 ...

.gitlab-ci.yml

  1. default:
  2. image: docker:19.03.8
  3. services:
  4. - mysql:5.7
  5. before_script:
  6. - docker info
  7. variables:
  8. MYSQL_DATABASE: test
  9. MYSQL_ROOT_PASSWORD: fa843c707ce26702
  10. DB_HOST: host001.dev.ia
  11. DB_USERNAME: developer
  12. stages:
  13. - test
  14. - build
  15. # - deploy
  16. unit_test:
  17. stage: test
  18. tags:
  19. - php
  20. script:
  21. - cp .env.test .env
  22. - composer install
  23. - php artisan key:generate
  24. - php artisan migrate
  25. - vendor/bin/phpunit
  26. build_image:
  27. stage: build
  28. variables:
  29. DOCKER_IMAGE_TAG: 'bennybi/team1-prj1'
  30. tags:
  31. - php
  32. script:
  33. - docker build --cache-from "${DOCKER_IMAGE_TAG}" -t "${DOCKER_IMAGE_TAG}" .
  34. - docker login --username $DOCKER_LOGIN --password $DOCKER_PASSWORD
  35. # - docker run my-docker-image /script/to/run/tests
  36. - docker push ${DOCKER_IMAGE_TAG}:latest
  37. rules:
  38. - if: $CI_COMMIT_BRANCH == "dev"
  39. # deploy_dev:
  40. # stage: deploy
  41. # tags:
  42. # - php
  43. # script:
  44. # - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
  45. # - eval $(ssh-agent -s)
  46. # - ssh-add <(echo "$SSH_PRIVATE_KEY")
  47. # - mkdir -p ~/.ssh
  48. # - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
  49. # # - ~/.composer/vendor/bin/envoy run deploy --commit="$CI_COMMIT_SHA"
  50. # environment:
  51. # name: dev
  52. # url: http://team1-prj1.dev.ia
  53. # when: manual
  54. # rules:
  55. # - if: $CI_COMMIT_BRANCH == "dev"

3 参考:

GitLab: automated build and publish of multi-platform container image with GitLab pipeline | Fabian Lee : Software Engineer

https://jaumemule.medium.com/build-a-php8-0-fpm-gitlab-ci-cd-application-docker-google-cloud-5f2868e8370

https://docs.gitlab.com/ee/ci/docker/using_docker_build.html

 - https://github.com/papertank/envoy-deploy/blob/master/readme.md

https://warrickbayman.medium.com/zero-downtime-laravel-deployments-with-envoy-version-2-227c8259e31c

Test and deploy Laravel applications with GitLab CI/CD and Envoy | GitLab

GitLab CI/CD examples | GitLab

使用GitLab Runner为基于Laravel的PHP项目进行部署 

GitLab CI/CD for Beginners [FREE Course] - DEV Community

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/192952
推荐阅读
相关标签
  

闽ICP备14008679号