Help us understand the problem. What is going on with this article?

CircleCIでOrbsを利用して並列処理を行う

More than 1 year has passed since last update.

この記事はCircleCI Advent Calendar 2018 3日目の記事です。

前回の記事は @yasuhiroki さんの「CircleCIのOnly build pull requestsをoffにしてもプルリクエストを作ったらJobを実行したい」でした

はじめに

この記事ではCircleCIの並列処理についてAnsibleを使って解説します。この記事での検証結果は CircleCI で確認できます。

この記事で解説する事

  • CircleCIの並列処理の基本的なやり方
  • CI/CDの処理時間を短縮する方法

この記事で解説しない事

  • Ansibleの使い方
  • CircleCIの使い方
  • Dockerの使い方
  • Orbsの解説、導入方法

この記事で言いたい事

  • Orbsを利用してパイプラインの再利用性を高めましょう
  • Orbsに定義出来ない処理、もしくはOrbsで定義すると再利用が難しくなる処理は「.circleci/config.yml」へ書きましょう
  • CircleCIで処理する必要がないものはあらかじめDockerイメージ化しレジストリへ登録しておきましょう(スナップショットの作成)
  • CircleCIで利用する独自のDockerイメージはcron等を用いて頻繁にアップデートしましょう(スナップショットのリフレッシュ)

Ansibleとは

Ansible(アンシブル)は、レッドハットが開発するオープンソースの構成管理ツールである。サーバを立ち上げる際、あらかじめ用意した設定ファイルに従って、ソフトウェアのインストールや設定を自動的に実行する事が出来る[1]。特に大規模なコンピュータ・クラスターを構築する時に、時間の短縮やミスの削減に有用である。構成管理に加え、オーケストレーションやソフトウェアデプロイメントの機能を持つ 

Ansible (ソフトウェア) - Wikipedia より引用

Ansibleについての注意点

Ansibleは通常OSやネットワーク機器に対して利用されます。この記事みたいにDockerで利用する場合 Ansibleの性能を100%引き出す事はできません

この記事ではあくまでCircleCIで並列処理を説明するための事例として取り上げました。

実際にAnsibleを利用する際はDockerではなくOS環境下で実行してください。

シナリオ

目標

複数の異なるディストリビューション複数の異なるバージョンのAnsible を利用しプログラムをインストールする処理のCIを行う。

CI/CDの速度を速めるためにOSイメージの「 スナップショット 」を利用し「 並列で処理 」をする。

処理の流れ

  1. Playbook取得
  2. 静的解析(lintを実行する)
  3. Playbook実行

想定するディストリビューション

  • CentOS7
  • Ubuntu18

利用するAnsibleのバージョン

  • 2.7系(aptもしくはyumコマンドでインストールされるAnsible)
  • 2.7系(pipコマンドでインストールされるAnsible)
  • 2.8系

全部で 「2系統のディストリビューション」 x 「3系統のAnsible」 の6系統の並列処理を行います。

circleci-2.png

インストールするプログラム

cowsay.gif

使用するAnsible Playbook

準備作業としてDockerイメージをレジストリサービスへ登録する

CI/CDの速度を上げるためOSイメージのスナップショットを作成しておきます。

1.Dockerfileを作成する

あらかじめ「CentOS7」と「Ubuntu18」に

  • Ansible
  • Ansible-lint
  • Git

をインストールしたDockerイメージを作成しレジストリへ登録します。

CentOS7へAnsible、Ansible-lint、GitをインストールするDockerfileの例


Dockerfile
FROM centos:latest

RUN set -x && \
    rm -f /etc/rpm/macros.image-language-conf && \
    sed -i '/^override_install_langs=/d' /etc/yum.conf && \
    yum -y reinstall glibc-common && \
    yum clean all && \
    yum update -y && \
    yum install -y epel-release git && \
    yum -y install ansible && \
    yum -y install expect python-devel python-pip && \
    pip install --upgrade pip && pip install --upgrade setuptools && \
    pip install ansible-lint

ENV LANG="ja_JP.UTF-8" \
    LANGUAGE="ja_JP:ja" \
    LC_ALL="ja_JP.UTF-8"


Ubuntu18へAnsible、Ansible-lint、GitをインストールするDockerfileの例


Dockefile
FROM ubuntu:latest

RUN apt update -y && \
    apt install -y language-pack-ja-base language-pack-ja && \
    apt install -y software-properties-common && \
    apt-add-repository -y ppa:ansible/ansible && \
    apt update -y && \
    apt install -y ansible && \
    apt install -y curl python-dev git && \
    curl "https://bootstrap.pypa.io/get-pip.py" -o "/tmp/get-pip.py" && \
    python /tmp/get-pip.py && \
    pip install --upgrade pip && pip install --upgrade setuptools && \
    pip install ansible-lint

ENV LANG="ja_JP.UTF-8" \
    LANGUAGE="ja_JP:ja" \
    LC_ALL="ja_JP.UTF-8"


2. Docker HubへDockerイメージを登録する

この記事ではレジストリに Docker Hub を利用します。

Docker HubGitHubへDockerfileをpushすると自動でDockerイメージをbuildするサービス を提供しています。

そのため

  • 記事等で利用する
  • OSSの活動で利用する

など「作成したDockerイメージの内容を開示したい」場合は有用なツールになります。

利用するDockerイメージ

Ansible Playbookを作成する

こちらのPlaybookを使用します。

Ansible Playbookでは以下の処理を定義しています。

  1. Playbookを実行するDockerコンテナのディストリビューション情報を取得する
  2. ディストリビューションによって処理を選択し実行する
install.yml
- hosts: all
  tasks:
    - name: Install cowsay with yum
      include_role:
        name: centos7
      when: ansible_distribution == "CentOS"

    - name: Install cowsay with apt
      include_role:
        name: ubuntu18
      when: ansible_distribution == "Ubuntu"
roles/centos7/tasks/main.yml
- name: Install cowsay with yum
  become: yes
  yum:
    name: cowsay
    state: present
roles/ubuntu18/tasks/main.yml
- name: Install cowsay with apt
  become: yes
  apt:
    name: cowsay
    state: present

- name: Copy cowsay execution file to '/usr/local/bin/'
  become: yes
  copy:
    src: /usr/games/cowsay
    dest: /usr/local/bin/cowsay
    owner: root
    group: root
    mode: 0755

orbを作成する

この記事ではCircleCI 2.1から本格的に利用できる Orbs を使います。

orbでの処理について

version: 2.1

commands:
  show-os-version:
    description: "Show OS version information"
    steps:
      - run:
          name: Show OS version
          command: |
            if [ -e /etc/os-release ]; then
              cat /etc/os-release
            elif [ -e /etc/redhat-release ]; then
              cat /etc/redhat-release
            else
              echo 'Invalid Image' 1>&2
              exit 1
            fi

  ansible-lint:
    description: "Execute ansible-lint command"
    steps:
      - run:
          name: Show ansible-lint version
          command: ansible-lint --version
      - run:
          name: Retrieve Ansible lint rules file
          command: |
            cd /tmp
            git clone https://github.com/TomonoriMatsumura/ansible_joke-programs_lint-rules
      - run:
          name: Execute ansible-lint
          command: |
            cd $CIRCLE_WORKING_DIRECTORY
            find . -type f -name '*.yml' | xargs ansible-lint -c /tmp/ansible_joke-programs_lint-rules/.ansible-lint-rules

  ansible-playbook:
    description: "Execute ansible-playbook command"
    steps:
      - run:
          name: Show Ansible version
          command: ansible --version
      - run:
          name: Execute Ansible Playbook
          command: |
            cd $CIRCLE_WORKING_DIRECTORY
            ansible-playbook -i localhost install.yml
      - run:
          name: Execute Ansible Playbook again, checking to make sure it's idempotent.
          command: |
            cd $CIRCLE_WORKING_DIRECTORY
            ansible-playbook -i localhost install.yml

executors:
  centos7-ansible-yum:
    docker:
      - image: tomonorimatsumura/centos7-ansible:yum
  centos7-ansible-stable:
    docker:
      - image: tomonorimatsumura/centos7-ansible:stable
  centos7-ansible-devel:
    docker:
      - image: tomonorimatsumura/centos7-ansible:devel
  ubuntu18-ansible-apt:
    docker:
      - image: tomonorimatsumura/ubuntu18-ansible:apt
  ubuntu18-ansible-stable:
    docker:
      - image: tomonorimatsumura/ubuntu18-ansible:stable
  ubuntu18-ansible-devel:
    docker:
      - image: tomonorimatsumura/ubuntu18-ansible:devel

orbにて以下の処理を定義します。

  1. 利用するDockerイメージのディストリビューション情報を表示する
  2. Ansible-lintを使いPlaybookのシンタックスをチェックする
  3. Playbookを実行する

これはcommandsセクションの

  1. show-os-version key
  2. ansible-lint key
  3. ansible-playbook key

がそれぞれ対応します。

またexecutorセクションでDockerイメージを定義しています。

.circleci/config.yml を作成する

.circleci/config.yml
show-os-version-steps: &show-os-version-steps
  steps:
    - ansible/show-os-version

lint-steps: &lint-steps
  steps:
    - checkout
    - ansible/ansible-lint

playbook-steps: &playbook-steps
  steps:
    - checkout
    - ansible/ansible-playbook
    - run:
        name: Execute cowsay
        command: |
          export TERM=xterm
          cowsay "hello world!"

version: 2.1

orbs:
  ansible: orbss/ansible@volatile

jobs:
  Ce7-yum_os-version:
    executor:
      name: ansible/centos7-ansible-yum
    <<: *show-os-version-steps
  Ce7-yum_lint:
    working_directory: /tmp/ansible
    executor:
      name: ansible/centos7-ansible-yum
    <<: *lint-steps
  Ce7-yum_playbook:
    working_directory: /tmp/ansible
    executor:
      name: ansible/centos7-ansible-yum
    <<: *playbook-steps

  Ce7-stable_os-version:
    executor:
      name: ansible/centos7-ansible-stable
    <<: *show-os-version-steps
  Ce7-stable_lint:
    working_directory: /tmp/ansible
    executor:
      name: ansible/centos7-ansible-stable
    <<: *lint-steps
  Ce7-stable_playbook:
    working_directory: /tmp/ansible
    executor:
      name: ansible/centos7-ansible-stable
    <<: *playbook-steps

  Ce7-devel_os-version:
    executor:
      name: ansible/centos7-ansible-devel
    <<: *show-os-version-steps
  Ce7-devel_lint:
    working_directory: /tmp/ansible
    executor:
      name: ansible/centos7-ansible-devel
    <<: *lint-steps
  Ce7-devel_playbook:
    working_directory: /tmp/ansible
    executor:
      name: ansible/centos7-ansible-devel
    <<: *playbook-steps

  Ub18-apt_os-version:
    executor:
      name: ansible/ubuntu18-ansible-apt
    <<: *show-os-version-steps
  Ub18-apt_lint:
    working_directory: /tmp/ansible
    executor:
      name: ansible/ubuntu18-ansible-apt
    <<: *lint-steps
  Ub18-apt_playbook:
    working_directory: /tmp/ansible
    executor:
      name: ansible/ubuntu18-ansible-apt
    <<: *playbook-steps

  Ub18-stable_os-version:
    executor:
      name: ansible/ubuntu18-ansible-stable
    <<: *show-os-version-steps
  Ub18-stable_lint:
    working_directory: /tmp/ansible
    executor:
      name: ansible/ubuntu18-ansible-stable
    <<: *lint-steps
  Ub18-stable_playbook:
    working_directory: /tmp/ansible
    executor:
      name: ansible/ubuntu18-ansible-stable
    <<: *playbook-steps

  Ub18-devel_os-version:
    executor:
      name: ansible/ubuntu18-ansible-devel
    <<: *show-os-version-steps
  Ub18-devel_lint:
    working_directory: /tmp/ansible
    executor:
      name: ansible/ubuntu18-ansible-devel
    <<: *lint-steps
  Ub18-devel_playbook:
    working_directory: /tmp/ansible
    executor:
      name: ansible/ubuntu18-ansible-devel
    <<: *playbook-steps

workflows:
  version: 2
  ansible-playbook:
    jobs:
      - Ce7-yum_os-version
      - Ce7-yum_lint:
          requires:
            - Ce7-yum_os-version
      - Ce7-yum_playbook:
          requires:
            - Ce7-yum_lint


      - Ce7-stable_os-version
      - Ce7-stable_lint:
          requires:
            - Ce7-stable_os-version
      - Ce7-stable_playbook:
          requires:
            - Ce7-stable_lint


      - Ce7-devel_os-version
      - Ce7-devel_lint:
          requires:
            - Ce7-devel_os-version
      - Ce7-devel_playbook:
          requires:
            - Ce7-devel_lint


      - Ub18-apt_os-version
      - Ub18-apt_lint:
          requires:
            - Ub18-apt_os-version
      - Ub18-apt_playbook:
          requires:
            - Ub18-apt_lint


      - Ub18-stable_os-version
      - Ub18-stable_lint:
          requires:
            - Ub18-stable_os-version
      - Ub18-stable_playbook:
          requires:
            - Ub18-stable_lint


      - Ub18-devel_os-version
      - Ub18-devel_lint:
          requires:
            - Ub18-devel_os-version
      - Ub18-devel_playbook:
          requires:
            - Ub18-devel_lint

Anchors & Aliasesについて

anchorの設定 aliasの配置
show-os-version-steps: &show-os-version-steps <<: *show-os-version-steps
lint-steps: &lint-steps <<: *lint-steps
playbook-steps: &playbook-steps <<: *playbook-steps

表にある記述がある箇所は 「.circleci/config.yml」 で利用できるAnchors & Aliasesの機能です。anchorを定義した後aliasを指定した箇所へ配置できます。

詳しくはこちらを参考にして下さい。

workflowsでJobを並列実行する

workflowsセクションではそれぞれのJobを並列実行するように処理を書いています。CircleCIでJobを実行する順序を定義する詳しい方法はこちらを参考にして下さい。

利用するorbのバージョンについて

Orbsでは「circleci/config.yml」で参照するorbのバージョンを指定できます。ですがこの記事に関しては常に最新版を取得する volatile を使用しています。

実際にOrbsを利用する際には最新バージョンではなく特定のバージョン番号を指定した方が良い場合もあります。バージョンの指定の記述は適宜使い分けると良いでしょう。

まとめ

CircleCIでCIを並列実行する方法を紹介しました。この方法を利用すれば、例えばバージョン違いの Ruby + Rails や PHP + Laravel を利用し並列処理で検証を行う、と言うような事も可能になります。

フレームワークや言語のバージョンの互換性を考慮した開発が可能になるかもしれません。

CircleCIの並列処理のポイント

  • Orbsを利用してパイプラインの再利用性を高める
  • Orbsに定義出来ない処理、もしくはOrbsで定義すると再利用が難しくなる処理は「.circleci/config.yml」へ書く
  • CircleCIで処理する必要がないものはあらかじめDockerイメージ化しレジストリへ登録する(スナップショットの作成)
  • CircleCIで利用する独自のDockerイメージはcron等を用いて頻繁にアップデートする(スナップショットのリフレッシュ)

この辺りを意識すると良いと思います。特にコンテナを含む OSイメージのスナップショット は並列処理に次いでCI/CDのスピードに大きな影響を与えます。スナップショットは意識的に採用すべきでしょう。

今後の課題

並列処理のレポートの問題

今回使用した 「.circleci/config.yml」 ですが問題がひとつあります。それは並列処理だと実行するJobの順番が指定できないと言うことです。

理想的なパイプライン

circleci-2.png

実際のパイプライン

スクリーンショット 2018-11-29 17.37.29のコピー.png

パイプラインをリニアに表示したいのですがCircleCIは処理を実行した順にJobを表示するみたい?で意図したものと異なったものが出力されます。

この挙動を改善できると良いのですが今のところ方法が分かりません。

参考リンク

Ansible

CircleCI

Orbsについて

Anchor & Aliasesについて

並列処理について

記事の検証

Cowsay

Docker Hub

最後に

CircleCI Advent Calendar 2018 の明日の記事は @ganta さんです

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away