V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dataman
V2EX  ›  推广

如何用 Docker 实现 PHP 命令行程序的 CI/CD

  •  
  •   dataman · 2017-05-22 18:26:54 +08:00 · 1876 次点击
    这是一个创建于 2742 天前的主题,其中的信息可能已经有所发展或是发生改变。

    数人云今天带来的文章将分享如何用 Docker 实现 PHP 命令行程序的 CI/CD 过程中整体思路以及需要注意哪些问题。

    6 月 10 日,《 DevOps&SRE 超越传统运维之道》的话题将在北京延续,四位业界大牛技术齐聚,结合传统运维现状及实践案例,讲述 DevOps&SRE 的超越之道。点击即可报名。

    内容要点:

    • 使用 Jet 设置环境并在本地运行测试
    • 配置 Codeship Pro 每次新代码提交时,自动运行测试
    • 上一步的测试通过后,自动将更新部署到服务器

    持续集成

    应用程序和测试套件已经在本地运行,下一步要做的是建立一些持续集成系统。虽然可以设置服务器来执行此操作,但这个过程工作量略大,因此推荐一个像 Codeship Pro 的服务。

    使用 Jet 进行本地测试

    代码提交到 Codeship 测试前,建议先安装其本地版本的持续集成平台:Jet。这会更快地推进工作,下面示例配置文件中,需要根据应用的实际情况,做出相应调整。

    安装 Jet 后,在项目的根目录中创建两个新文件——

    1 )codeship-services.ymldocker-compose.yml文件的变种,适用于 Codeship

    2 )codeship-steps.yml – 在持续集成过程中,命令和顺序的说明

    codeship-services.yml文件与 docker-compose.yml几乎一样。内容如下:

    version: "2.0"
    services:
      # PHP Application
      app:
        build: .
        links:
          - database
        encrypted_env_file: .env.encrypted
        command: cron -f
      # Database
      database:
        image: mariadb
        encrypted_env_file: .env.encrypted
      # Composer
      composer:
        image: composer/composer
        volumes:
          - ./:/app
    

    codeship-steps.yml内容如下,这个文件在例子里很简单,按顺序执行这些的命令(一个接一个地)就好。如果应用程序允许,也可以并列地跑某几个步骤:

    -type: serial
      steps:
      -service: composer
        command: install
      -service: app
        command: bash docker/codeship-run.sh
    

    为了确保应用程序容器和数据库容器已启动,可以看到 codeship-steps.yml 文件调用了一个尚未创建的 shell 脚本。若数据库迁移,说明测试通过。把脚本放在 ./docker/codeship-run.sh,内容如下:

    #!/usr/bin/env bash
     
    ## Ensure that the database is up and running
    function test_database {
      mysqladmin -h"$DB_HOST"-u"$DB_USERNAME"-p"$DB_PASSWORD"ping
    }
     
    count=0
    until( test_database )
    do
      ((count++))
      ## This will check up to 100 times.
      if[${count}-gt 100]
      then
        echo"Services didn't become ready in time"
        exit1
      fi
      ## And the script waits one second between each try
      sleep1
    done
     
    ## Create the database
    mysql -h"$DB_HOST"-u"$DB_USERNAME"-p"$DB_PASSWORD"-e 'CREATE DATABASE IF NOT EXISTS laravel'
     
    ## Run migrations
    php artisan migrate
     
    ## Run the test suite
    vendor/bin/phpunit
    

    首先,该脚本会尝试连接到数据库。Codeship 软件会自动启动应用程序容器和数据库容器,但 MySQL 初始化需要几秒,所以必须重试该 test_database()功能,直到成功连接数据库(或尝试 100 次)。这在 Codeship 的 Docker 文档中有更详细的概述。

    一旦脚本能连接到数据库,它将创建默认数据库(数据库名为 laravel )。然后会运行迁移,通过 PHPUnit 创建数据库表和测试套件。

    最后,为了测试配置正常运行和且结果通过,用 Jet 来运行所有步骤:

    $ jet steps
    

    如果一切正常,那么在构建容器镜像过程中,会看到一堆输出,运行返回一条成功消息:

    {ContainerRunStdout=step_name:"serial_bash_docker/codeship-run.sh" service_name:"app"}: PHPUnit 5.7.19 by Sebastian Bergmann and contributors.
    {ContainerRunStdout=step_name:"serial_bash_docker/codeship-run.sh" service_name:"app"}:.                                                                   1/1(100%)
     
    Time:1.09 seconds, Memory:12.00MB
     
    OK (1test,1 assertion)
    {StepFinished=step_name:"serial_bash_docker/codeship-run.sh"type:STEP_FINISHED_TYPE_SUCCESS}
    $
    

    将仓库连接到 Codeship

    如果尚未提交本地代码到 GitHub 或 Bitbucket 上。每次提交代码更改时,Codeship 都自动会从私有或公共仓库中拉取代码,所以只需要设置 Codeship 去监视那个仓库。

    在 Codeship 中创建一个新项目,并将其连接到仓库:

    image

    跳出提示框时,选择 Codeship Pro 做为项目类型。

    image

    现在项目已经链接到 Codeship。下次提交代码时,Codeship 将会编译且使用与本地相同的codeship-steps.yml来运行。此时唯一的问题是使用本地的 .env文件,此文件不会提交到代码仓库,不过有个简单方法来设置环境变量且又不会影响安全性。

    加密环境变量

    因为最好的做法是不要将**.env文件推送到持续集成服务器,所以需要提出一种安全地将变量传递给 Codeship 方法——加密.env**文件。

    首先,在 Codeship 找到 AES 密钥(通常位于项目设置的常规页面中),并将其放在一个名为codeship.aes的本地根目录文件中。不要忘记将此文件添加到.gitignore,因为它是不应共享的密钥:

    image

    下一步,更新codeship-services.yml文件,让其使用加密的.env 文件,而非明文.env 文件:

    version:"2.0"
    services:
      # PHP Application
      app:
        build: .
        links:
          - database
        encrypted_env_file: .env.encrypted
        command: cron -f
      # Database
      database:
        image: mariadb
        encrypted_env_file: .env.encrypted
      # Composer
      composer:
        image: composer/composer
        volumes:
          - ./:/app
    

    使用 Jet 将.env 文件加密成.env.encrypted,将加密后的.env.encrypted 文件提交到软件仓库,然后将其推送到远程仓库:

    $ jet encrypt .env.env.encrypted
    $ git add -A &&git commit -am "Adding codeship config"
    $ git push origin
    

    Codeship 正在编译代码:

    image

    也可以点击进去,查看编译详细过程和每个步骤:

    image

    如果上述都无误地执行,最后会看到一个成功的编译结果:

    image

    自动部署

    虽然持续集成服务会让人知道编译和测试是否通过,但使用 Codeship 更大的价值在于自动化部署过程,为了做到这一点,需要做几件事情:

    • 在服务器上手动配置和部署代码库
    • 服务器上有一个 SSH 密钥,允许从代码库拉取代码
    • 服务器上有一个脚本,用来更新代码并重新启动容器

    准备齐全后,就可以构建一个部署者容器,其工作就是在编译过程结束后,登陆到 SSH 服务器中,运行更新脚本。

    这只是利用容器来部署代码的一种方法,也许并不是最佳的方法。另一个选择是使用 Docker Hub 等容器仓库来编译,然后直接从 Docker Hub 那里更新容器。Docker 在生产中的最佳做法仍在探索中,这种方法更适用也相对简单。

    首次手动部署代码

    此步骤根据主机服务提供商而有所不同,但只要服务器满足安装了 Git,Docker 和 Docker Compose 就可以。SSH 登陆服务器:

    • 创建一个新的 SSH 密钥
    • 赋予 SSH 密钥可以从代码库读取访问
    • 克隆代码库
    • 设置.env 文件,使用一个新的 APP_KEY 和数据库密码
    • 使用 docker-compose up -d – build 命令来制作镜像,并且运行容器

    现在可以运行docker ps查看,当第一次在本地配置好这个项目时,可以看到同样的两个容器在运行。

    添加脚本更新服务器代码

    现在在本地版本的代码中,添加一个 shell 脚本,该脚本将从仓库中获取更新的代码,并重启容器:

    #!/usr/bin/env bash
     
    ## Pull the latest code
    git pull origin master
     
    ## Rebuild the containers
    docker-compose up -d --build
     
    ## Run migrations
    docker exec dockerphpcliexample_app_1 php artisan migrate --force
    

    文件名叫 deploy.sh 位于 docker/文件夹中。

    此时,要确保文件位于服务器上,因此提交代码到代码库,又将其从服务器中拉出。通过在服务器上运行命令: $ bash docker/deploy.sh,并确保容器仍然工作。

    创建一个部署者容器

    综上所述,在编译和测试结束后,现在需要一个容器在 Codeship 的 CI 服务器远程运行此部署脚本。在仓库中创建一个名为 deployer/ 的新目录,里面包含 Dockerfile 文件,.env 文件和 execute.sh 文件。

    Dockerfile:

    FROM alpine:latest
     
    # Install openssh
    RUN apk update && apk add openssh
     
    # Prep for the ssh key
    RUN mkdir-p "$HOME/.ssh"
    RUN touch$HOME/.ssh/id_rsa
    RUN chmod600$HOME/.ssh/id_rsa
     
    # Add the shell script
    COPY execute.sh execute.sh
     
    CMD sh execute.sh
    

    .env:

    USER=<SERVER_SSH_USERNAME>
    HOST=<SERVER_HOST>
    PRIVATE_SSH_KEY=<SSH_KEY (with linebreaks replaced with `\n`)>
    

    execute.sh

    #!/usr/bin/env bash
    echo-e $PRIVATE_SSH_KEY>>$HOME/.ssh/id_rsa
    ssh-t -oStrictHostKeyChecking=no $USER@$HOST"cd docker-php-cli-example && sh docker/deploy.sh"
    

    这个容器将使用.env 文件中的环境变量,SSH 登陆到服务器运行部署脚本。

    让 Codeship Pro 运行部署者容器

    为了让 Codeship 知道部署者容器,把它添加到 codeship-services.yml 文件中:

    # Deployer
      deployer:
        build: ./deployer
        encrypted_env_file: deployer/.env.encrypted
    
    codeship-steps.yml 文件:
    
    -service: deployer
        command: sh execute.sh
    

    加密 deployer/.env 文件,这样可以提交到代码库,并且不会暴露服务器的 SSH 密钥。正如对主代码库那样,用 jet 来加密.env 文件:

    $ jet encrypt deployer/.env deployer/.env.encrypted
    

    最后,把更新的代码推到 github 仓库,确保 Codeship 成功地编译和部署了代码:

    原文地址: https://blog.codeship.com/adding-ci-and-cd-to-a-php-command-line-app-with-docker/

    活动推荐:

    image Meetup 北京|DevOps&SRE 超越传统运维之道 DevOps&SRE 超越传统之道北京站来啦! 6 月 10 日,四位业界大牛技术齐聚,结合传统运维现状及实践案例,讲述 DevOps&SRE 的超越之道。

    活动报名地址: http://www.bagevent.com/event/596024

    1 条回复    2017-05-22 22:28:51 +08:00
    mingyun
        1
    mingyun  
       2017-05-22 22:28:51 +08:00
    编辑这么大段也是不易
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2605 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 04:41 · PVG 12:41 · LAX 20:41 · JFK 23:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.