PHPエンジニアのためのJenkins2入門

# タイトルは釣り

- あまりPHP関係なかった



---

# Jenkins
+ https://jenkins.io/
+ オープンソースCIサーバ
+ 高機能cron

---

# インストール
<img width="809" alt="スクリーンショット 2017-02-08 15.22.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/31709/14deca0b-ad05-aede-e5a8-7facdb7598f5.png">

+ Weekly Release
    + 毎週リリースされる最新版
+ LTS Release
    + 12週間ごとにリリースされる安定版
    + 脆弱性とかがあるときは随時リリース


---

# ダウンロードして java -jar

    $ wget http://mirrors.jenkins-ci.org/war/latest/jenkins.war
    $ java -jar jenkins.war

---
# Dockerでrun

    docker run --rm -u root \
        -p 8080:8080 -p 50000:50000 \
        -v /usr/bin/docker:/usr/local/bin/docker \
        -v /var/run/docker.sock:/var/run/docker.sock \
        -v jenkins_home:/var/jenkins_home \
        jenkins

- https://hub.docker.com/_/jenkins/

---

# あとはウィザードに従って

---

# 軽くデモ

---

## Docker
+ CIとの相性が良い
    + 秘伝のタレを継ぎ足した環境ではなく、クリーンな環境でビルドできる
+ Jenkins自体の管理が楽になる
    + 秘伝のタレを継ぎ足したJenkinsサーバではなくなる
    + Jenkinsサーバが壊れたときにすぐに復旧できるか？
+ Dockerでビルドできるようにしておくと他のCIサービスにも移行できる
+ PHPは環境を作るのに手間がかかる(個人の感想です)

---

## プラグイン
<img width="877" alt="スクリーンショット 2017-02-08 15.30.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/31709/f36aada6-9d30-ceca-98cd-3574e4261b9e.png">

+ https://plugins.jenkins.io/
+ 1300+のプラグイン (2017/02/08時点)
+ プラグインで色々なビルドをサポート
+ 今日はプラグインの話はほとんどありません

---

## EC-CUBE
<img width="1022" alt="スクリーンショット 2017-02-08 16.20.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/31709/67155a4d-48da-3d0e-6796-698dca89897e.png">

- http://www.ec-cube.net/
- 国産のオープンソースECサイト構築パッケージ
- EC-CUBE本体の開発はTravisCIを使っている
    - https://travis-ci.org/EC-CUBE

---

## 今日のお話

- ビルド・パイプライン編
- 高機能cron編

---

## ビルド・パイプライン編

---
## Pipeline
<img width="1022" src="https://jenkins.io/doc/book/resources/pipeline/realworld-pipeline-flow.png">

- ビルド・パイプラインをDSLで記述できる

---

### ノード

    // マスターノードで実行
    node {
       echo 'Hello World'
    }

    // 'windows'とラベル付けされたノード上で実行
    node('windows') {
       echo 'Hello Windows'
    }

---

### ステージ

    node {
        stage('最初のステージ') {
            // このステージで行う処理を記述
        }
        stage('次のステージ') {
            // このステージで行う処理を記述
        }
    }

![スクリーンショット 2017-02-08 18.18.28.gif](https://qiita-image-store.s3.amazonaws.com/0/31709/57a3657a-2ead-5de5-c444-5edc4c26a73d.gif)

---

![スクリーンショット 2017-02-08 18.20.33.gif](https://qiita-image-store.s3.amazonaws.com/0/31709/e2778f8b-e73e-ae26-3fee-8e8d12346a6d.gif)

---

# /pipeline-syntax Pipeline Syntax

<img width="1279" alt="スクリーンショット 2017-02-08 16.33.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/31709/3996b739-15ea-39b4-915f-1552ab70a542.png">

- パイプラインスクリプトの記述方法を教えてくれる便利機能
- プラグインごとに記述方法が異なるのでこの機能を使ってスニペットを作成する

---

# /pipeline-syntax/globals グローバル変数

<img width="1274" alt="スクリーンショット 2017-02-08 16.34.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/31709/b453477d-787e-1cfc-1b65-efc6abe86498.png">

- パイプラインスクリプトで使用できる変数一覧

---

# /credentials 認証情報

- パスワードや秘密鍵などを管理できる機能
- パイプラインスクリプトにパスワード等を直書きしなくてよくなる
- この機能で管理させることでJenkinsサーバに手動で仕込んでおく必要がなくなる
- JENKINS_HOMEを安全にバックアップしておけば、リストア時の手間が省ける

---

# EC-CUBEのテストをする

---

# EC-CUBEのテストパイプライン
- ソースのチェックアウト
- テストに使うコンテナ準備
    - DBサーバ
    - メールサーバ
- 前準備
    - composerによるライブラリインストール
    - EC-CUBE初期化
- テスト実行
    - PHPUnit
- コンテナ削除
- レポート
    - JUnitレポートの保存
    - Slack通知

---

## チェックアウト

    stage('Git checkout') {
        git credentialsId: 'github', url: 'git@github.com:kiy0taka/ec-cube.git'
    }

---

## コンテナの準備

    stage('Start containers') {
        postgres = docker.image("--name ${JOB_NAME}-${BUILD_NUMBER}-db -e TZ=Asia/Tokyo postgres:latest").run()
        mailcacher = docker.image('--name ${JOB_NAME}-${BUILD_NUMBER}-mail schickling/mailcatcher').run()
        sleep 3
    }

- `docker run オプション イメージ` -> `docker.image(オプション イメージ).run()`
- 並列実行できるよう、コンテナに一意な名称をつける
- `Container.run()` 非同期っぽいので `sleep 3`
- runしたコンテナはちゃんとstopさせないといけないので`try-finally`で囲っておく

---

## テストの実行

    docker.image('eccube/php7-ext-apache').inside("""
            -e TZ=Asia/Tokyo -e DBSERVER=db -e DBUSER=postgres -e MAIL_HOST=mail
            --link ${JOB_NAME}-${BUILD_NUMBER}-db:db --link ${JOB_NAME}-${BUILD_NUMBER}-mail:mail""") {

        stage('Composer install') {
            sh 'composer install --dev --no-interaction -o'
        }

        stage('EC-CUBE install') {
            sh 'php ./eccube_install.php pgsql none'
        }

        stage('PHPUnit Test') {
            sh 'vendor/bin/phpunit --log-junit junit.xml'
        }
    }

- `docker.image('イメージ').inside('オプション') { 処理 }` マジ便利
- `{ 処理 }`はコンテナ内で実行される
- `{ 処理 }`が終わるとコンテナが破棄される
- `phpunit --log-junit`

---

#### コンテナを落とす

    stage('Stop containers') {
        postgres.stop()
        mailcacher.stop()
    }

- `finally`ブロックで確実に

---

#### ビルド結果の通知

    stage('Report') {
        // JUnitレポートの保存
        junit 'junit.xml'
        // Slack通知
        slackSend "${JOB_NAME} ${BUILD_NUMBER} ${currentBuild.result} (${BUILD_URL})"
    }

- Slack通知は[Slackプラグイン](https://wiki.jenkins-ci.org/display/JENKINS/Slack+Plugin)を利用

---

### 全体像


    node {

        stage('Git checkout') {
            git credentialsId: 'github', url: 'git@github.com:kiy0taka/ec-cube.git'
        }

        try {
            stage('Start containers') {
                containerPrefix = "${JOB_BASE_NAME}-${BUILD_NUMBER}"
                postgres = docker.image("--name ${containerPrefix}-db -e TZ=Asia/Tokyo postgres:latest").run()
                mailcacher = docker.image("--name ${containerPrefix}-mail schickling/mailcatcher").run()
                sleep 3            
            }

            docker.image('eccube/php7-ext-apache').inside("""
                    -e TZ=Asia/Tokyo -e DBSERVER=db -e DBUSER=postgres -e MAIL_HOST=mail
                    --link ${containerPrefix}-db:db --link ${containerPrefix}-mail:mail""") {

                stage('Composer install') {
                    sh 'composer install --dev --no-interaction -o'
                }

                stage('EC-CUBE install') {
                    sh 'php ./eccube_install.php pgsql none'
                }

                stage('PHPUnit') {
                    sh 'vendor/bin/phpunit --log-junit junit.xml'
                }
            }
            currentBuild.result = 'SUCCESS'
        } catch (e) {
            currentBuild.result = 'FAILURE'
        } finally {
            stage('Stop containers') {
                postgres.stop()
                mailcacher.stop()            
            }

            stage('Report') {
                junit 'junit.xml'
                slackSend "${JOB_NAME} ${BUILD_NUMBER} ${currentBuild.result} (${BUILD_URL})"
            }
        }

    }

---

### Github Organization Folder
- GithubのOrganizationやユーザのリポジトリをスキャンしてブランチごとにジョブを作ってくれる機能
- リポジトリにJenkinsfileを入れておく


        stage('Git checkout') {
            git credentialsId: 'github', url: 'git@github.com:kiy0taka/ec-cube.git'
        }

        stage('Git checkout') {
            checkout scm
        }

---

## デモ

---

# 高機能cron編

---

## 高機能cron
- スケジューリング
- ロギング
- 通知
- 手動実行
- etc.
- http://rundeck.org/ とかありますが

---

### デモ DBバックアップ
- SSHでサーバに入ってデータベースをダンプする
    - パイプラインジョブではなく昔ながらのフリースタイル
    - [SSH Agent プラグイン](https://wiki.jenkins-ci.org/display/JENKINS/SSH+Agent+Plugin)使用

---

### その他

- デプロイ
    - git pull
- 監視
    - curl
- DBリストア
    - リストアできるのを確認するまでがバックアップ
- バッチ処理
    - php app/console xxx
- SSL証明書更新
    - Let's Encrypt

---

# まとめ
- パイプラインスクリプトはYAMLじゃないので複雑かも
- スクリプトを書けるので柔軟性が高い
- Jenkinsfileにすることでバージョン管理も可能に
- 面倒な(個人の感想です)PHPのCI環境もJenkins+Dockerで