ConcourseCIの紹介

コンコースCIロゴ







みなさんこんにちは! Habréでは、 ConcourseCIなどのアセンブリサーバーに関する情報がほとんどないことに気付きました。 このギャップを埋めて簡単な紹介文を書くことにしました。 このツールの猫の説明と小さなチュートリアルの下。







したがって、ConcourseCIはCI / CDビルドサーバーです。 Pivotalによって開発されており、現在活発に開発されています。 会社自体にはこのプロジェクトにフルタイムで取り組んでいる人が何人かいるため、現在、新しいバージョンがかなり定期的にリリースされており、追加機能が積極的に実装され、バグが迅速に修正されています。 当初、最も頻繁に起こるように、Pivo​​talはJenkinsに不満だったため、このツールを社内で使用するために作成しましたが、後にこのプロジェクトをGithubに公開し、コミュニティが引き継いだため、リリースはしばしばコミュニティのサードパーティ開発者からの新しい機能を思い付きます。 完全にGoで書かれています。







この記事では:









長所



この製品の主な利点をリストします。









コンコースCIメビンビューメイビンビュー







新しいデザインは現在ベータモードですが、コミュニティによって活発に議論されているため、私のスクリーンショットは近い将来に時代遅れになる可能性があります。 新しいインターフェイスは次のようになります。







ConcourseCI新しいベータデザン









設置



前述したように、プロジェクトはGoで記述されているため、このリリースに単一のバイナリファイルが付属しており、1つのコマンドで実行できます。 しかし、より良い方法があります。 ConcourseCIは公式のdocker imageを提供するため、docker-composeを使用して、1つのチームだけでプロジェクトを開始できます。







開始されたConcourseCIは、3つの部分で構成されています。







  1. PostgreSQLデータベース
  2. コンコースWeb 。 これは一種のマスターです。 グラフィカルWebインターフェイスと、最も重要なことは、航空から借用した ATC「ATC」または「航空交通管制」という用語:これは、管制官がフライトを監視する滑走路近くの空港にあるフライトコントロールタワーです)。 ATCはリソースを割り当て、さまざまなタスクを実行し、クラスターを監視します。 実際のアセンブリはConcourse Webでは発生せずアセンブリを管理するだけで、アクセス可能なワーカーにタスクを委任することに注意してください。 メインのConcourse Webシステムは1つだけ存在できます。
  3. コンコースワーカー -ここで実際の作業が行われます。 ワーカーは、ATCからタスクを受け取り、それを実行して、結果を報告します。 システムには、十分な鉄があるのと同じ数のそのようなワーカーが存在する可能性があります。 1つのサーバー/インスタンスで1つのワーカーを実行することをお勧めします。


したがって、完全に機能するサーバーは、1つのdocker-composeコマンドで起動できます。 ただし、ATCは暗号化されたチャネルを介してワーカーと通信し、開始する前に両側でキーをスリップする必要があるため、最初の開始前にキーを生成する必要があります。 次のようなキーを生成します。







mkdir -p keys/web keys/worker ssh-keygen -t rsa -f ./keys/web/tsa_host_key -N '' ssh-keygen -t rsa -f ./keys/web/session_signing_key -N '' ssh-keygen -t rsa -f ./keys/worker/worker_key -N '' cp ./keys/worker/worker_key.pub ./keys/web/authorized_worker_keys cp ./keys/web/tsa_host_key.pub ./keys/worker
      
      





その後、このファイルを使用してシステム全体を起動できます(公式ファイルはこちらを参照 )。







 version: '3' services: concourse-db: image: postgres:9.6 environment: POSTGRES_DB: concourse POSTGRES_USER: concourse POSTGRES_PASSWORD: changeme PGDATA: /database concourse-web: image: concourse/concourse links: [concourse-db] command: web depends_on: [concourse-db] ports: ["8080:8080"] volumes: ["./keys/web:/concourse-keys"] restart: unless-stopped # required so that it retries until concourse-db comes up environment: CONCOURSE_BASIC_AUTH_USERNAME: concourse CONCOURSE_BASIC_AUTH_PASSWORD: changeme CONCOURSE_EXTERNAL_URL: "${CONCOURSE_EXTERNAL_URL}" CONCOURSE_POSTGRES_HOST: concourse-db CONCOURSE_POSTGRES_USER: concourse CONCOURSE_POSTGRES_PASSWORD: changeme CONCOURSE_POSTGRES_DATABASE: concourse concourse-worker: image: concourse/concourse privileged: true links: [concourse-web] depends_on: [concourse-web] command: worker volumes: ["./keys/worker:/concourse-keys"] environment: - CONCOURSE_TSA_HOST=concourse-web:2222
      
      





Webインターフェイスを使用できるWebアドレスの実際の値を変数CONCOURSE_EXTERNAL_URL



にエクスポートすることを忘れないでください。







 export CONCOURSE_EXTERNAL_URL=http://192.168.99.100:8080
      
      





ご覧のとおり、この例ではすべてを1台のマシンで実行していますが、誰もこれに制限するものではなく、ニーズに応じて分散クラスターを安全に作成できます。







起動後、ブラウザでサーバーアドレスを開くことができ(上記の例-http://192.168.99.100:8080 )、まだ空のConcourse Webが表示されます。







インストールの詳細については、 こちらをご覧ください







3つのコンセプト



ですから、ビジネスに取りかかる前に、お互いを理解するために用語を見てみましょう。 ConcourseCIは3つの基本概念で動作します。







  1. リソース -アセンブリの材料を提供できるリソース、またはアセンブリの結果によって更新できるリソース。 典型的な例:git / hgリポジトリ、docker-register、FTP、S3、チャット、電子メール、電報ボットなど。 完全なリストはこちらです 。さらに、Githubの広大さでいつでも何かを見つけることができ、極端な場合は自分で書くことができます 。 リソースは特定の作業を行わず、さらなるアクションのために資料を「提供する」だけであることを理解することが重要です。







  2. タスクは、ビルドの一部として実行できる作業単位です。 タスクは、選択したdockerコンテナーで起動されます。







  3. ジョブ -タスク。 リソースとタスクを1つに結合します。 つまり、割り当て内でリソースを取得し、何らかの形でタスクで処理し、リソースを収集して結果を更新します。 1つのタスクは完全に分離されており、何度でも別々に実行できます。


Webインターフェイスの外観



そして、ブラウザーではConcourseCIは次のようになります。







ConcourseCIの単純なパニプレンの例







これはパイプラインの非常に単純な例ですが、ここではGitリポジトリからソースを取得してプロジェクトの構築を開始し、ドッカーイメージを作成し、適切なリソースを使用して電報チャネルの全員に通知する方法を見ることができます。







このスクリーンショットでは、リソースとタスクのみが表示されますが、タスクは表示されません。 ただし、タスクの1つ(この例では1つ)をクリックすると、緑色の四角酸塩の内部で何が起こっているかを確認できます。







コンコースCIオープンチンチ







この図では、数字にマークが付けられています。







  1. 手動でタスクを開始するボタン。 いつでも再実行できます
  2. 以前のすべてのビルドは、履歴の形式で見出しの下に長いチェーンで配置されます
  3. ソースを取得するリソース。 下矢印は「取得」を意味します。つまり、リソースから素材を「取得」します。
  4. 1つのタスクの作業の終了。 このリンクを開いて、プロジェクトのアセンブリ中に発生したコンテナのstdoutログを確認できます。
  5. リソース(dockerイメージ)を収集し、プライベートレジスタにドロップします。 上矢印はプットを意味します。つまり、リソースを更新しています
  6. 最後に、私達は私用電報チャネルでメッセージを投げます


このインターフェイスでは、すべてのパイプラインとタスクを表示し、結果を監視できます。







コンベヤーの完全に「高度な」バージョンの例として、 ConcourseCI自体コンベヤーを引用できます (はい、驚くべきことに、彼自身がそれを自分で組み立てるつもりです:))







しかし、前述したように、ここのWebは読み取り専用です。 合理的な疑問が生じます。新しいコンベアをプロジェクトに追加する方法は? 飛ぶことを紹介する時です。







飛ぶ



Flyは、ターミナルからConcourseCIクラスター全体を管理できるコマンドラインユーティリティです。 稼働中のコンピューターにインストールされ、サーバーを管理します。 これにより、必要なすべての操作を実行し、クラスターを維持できます。







このコマンドのヘルプ出力(小さな部分):







 $ fly help Usage: fly [OPTIONS] <command> Application Options: -t, --target= Concourse target name -v, --version Print the version of Fly and exit --verbose Print API requests and responses --print-table-headers Print table headers even for redirected output Help Options: -h, --help Show this help message Available commands: abort-build Abort a build (aliases: ab) builds List builds data (aliases: bs) check-resource Check a resource (aliases: cr) checklist Print a Checkfile of the given pipeline (aliases: cl) containers Print the active containers (aliases: cs) destroy-pipeline Destroy a pipeline (aliases: dp) destroy-team Destroy a team and delete all of its data (aliases: dt) execute Execute a one-off build using local bits (aliases: e) .....  ..
      
      





チームはGoで記述された1つのバイナリでもあります。 githubからダウンロードできますが、Webインターフェースの下部にあるリンクを使用してダウンロードする方が便利です。

ダウンロードフライ

/usr/bin/fly



にドロップして実行します。







これで、flyがインストールされました。 ConcourseCIを開始するには、サーバーに対して認証する必要があります。 これは非常に簡単に行われます:







$ fly --target office login --concourse-url=http://ci.your.concouce.server.com









どこで:









コマンドを入力すると、ユーザー名とパスワードの入力を求められます。 インストール中にdocker-compose.ymlファイルで指定したデータを入力します( CONCOURSE_BASIC_AUTH_USERNAME



およびCONCOURSE_BASIC_AUTH_PASSWORD



、上記を参照)







$ fly help



またはドキュメントページで入力すると、使用可能なすべてのコマンドのリストを見つけることができます







ログインに成功すると、すでに作業を開始できます。 このコマンドで最初のパイプラインを作成できるとしましょう:







$ fly -t office sp -c pipeline.yml -p my-pipeline-name









ここで:









このコマンドを実行した後、ブラウザーでConcourseCIを開き、新しいパイプラインを確認できます。 最初は一時停止されます。 新しいパイプラインをアクティブにするには、ブラウザの青い「▸」ボタンをクリックします







ConcourseCIは一時的に停止しましたパプリアンを作成しました







またはチームを使用する







$ fly -t office unpause-pipeline -p my-pipeline-name









パイプライン構成



パイプラインの記述がどのように進行しているかを検討する時が来ました。 ファイルはYAML形式で記述され、プロジェクトのソースと同じリポジトリに、つまりコードに「近い」ように保存できます。 言い換えれば、各プロジェクトは「自分自身を組み立てる方法を知っている」と言うことができます。これは、構成がその中にあるためです。 これは非常に便利です。各プログラマーは完全なアセンブリの詳細と微妙さを確認できるため、開発に役立つことは間違いありません。







ファイルは、リソースの宣言(リソース)とタスクの説明(ジョブ)のいくつかの部分に分割できます。 たとえば、Javaプロジェクトの単純なパイプラインを作成しましょう。 この場合、Gitリポジトリからソースを取得し、Gradleを使用してテストを実行してプロジェクトをビルドし、ビルド結果をAmazon S3クラウドにドロップします。







リソースから始めましょう。 それらはリソースのセクションで説明されてます。 この場合、GitリポジトリとAWS S3の2つのリソースを宣言する必要があります。







 resources: - name: source-code type: git source: uri: git@your-project.git branch: master private_key: | ......... - name: aws-s3-release type: s3 source: bucket: releases regexp: directory_on_s3/release-(.*).tgz access_key_id: ........ secret_access_key: ........
      
      





任意の名前(-name)を付けます。これにより、これらのリソースにアクセスし、同じ名前でブラウザーにリソースが表示されます。







次に、3つのことを行う1つのジョブを作成します。







  1. 宣言されたsource-code



    リソースからsource-code



  2. プロジェクトを集める
  3. aws-s3-release



    デプロイする


タスクについては、 jobs



セクションで説明しています。好きなだけ作成できます。 簡単な例では、「プロジェクトのビルド」という名前のタスクを1つだけ実行しています。







 jobs: - name: "Build project" plan: #   - get: source-code trigger: true #   ,    #    - task: "Build gradle project" config: platform: linux image_resource: type: docker-image source: {repository: "chickenzord/alpine-gradle", tag: "latest" } inputs: - name: source-code outputs: - name: result-jar run: path: sh args: - -exc - | cd source-code gradle test gradle build #    output, #       cp build/libs/app.jar ../result-jar #    S3 - put: aws-s3-release params: file: result-jar/app.jar acl: public-read
      
      





この例をさらに詳しく見ていきましょう。







リソースのすべては明確です。それを-getとして宣言し- get



。これは、データを取得することを意味します。 trigger: true



パラメーターは、リソースが更新されるたびにタスク自体が起動されることを意味します(この場合、誰かがコミットし、コミットを開始したことを意味します)。 必要に応じて、より具体的なパラメーターを指定できます。これは、正しいリソースのドキュメントに記載されています







タスク(タスク)。 ご存知のように、ここで実際のアクションが行われます。 タスクには、留意すべき2つのプロパティがあります。







  1. 受信および送信データ( 入力および出力 )を持つことができます
  2. Dockerコンテナーで開始します(実際には、すべてがコンテナーで開始されますが、タスクでは明示的にイメージを指定する必要があります)。


この例では、1つの入力ソースのみを指定しました。







  inputs: - name: source-code
      
      





受信ソースは、リソースまたは以前に起動された別のタスクのいずれかです(たとえば、チェーンでタスクを実行し、中間結果を次のタスクに渡すことができます)。 実際には、これはコンテナー内に同じ名前のフォルダーがあり、ConcourseCIがこのフォルダー内にリソースのコンテンツ(この場合はgitリポジトリーのコンテンツ)をきちんと配置することを意味します。 そのため、タスクの本文で最初に行ったことは、このフォルダー( cd source-code



)に移動することでした。ここで、Javaプロジェクトのソースコードを取得します。







また、アセンブリに使用するdockerイメージを指定する必要があります。 ここでのロジックは非常に単純です。特定のプロジェクト用のすべてのビルドツールがあるコンテナが必要です。 私の場合、Javaのプロジェクトはgreadによってアセンブルされるため、コンテナー内には必要なバージョンのJavaとgread自体が必要だと言えます。 たとえば、 この画像は機能し、私のタスクに最適です。 このイメージを次のように構成で指定します。







 config: platform: linux image_resource: type: docker-image source: {repository: "chickenzord/alpine-gradle", tag: "latest" }
      
      





このコンテナ内で、Gredlのタスクを起動できます。これはデモとして行います。







  cd source-code gradle test gradle build
      
      





はい、ビルド環境に非常に特定の要件がある場合、独自のドッカーイメージを収集してプライベートレジスタに保存することは難しくありません。ConcourseCIはどこからでも簡単にイメージをアップロードできます。







この例では、1つのYAMLファイルで可能な限り明確になるようにすべてを十分に詳細に説明したことにも注意してください。 ただし、タスクは別のYAMLファイルに移動できます 。 異なるプロジェクトでタスクを再利用するのはとても便利です。 この場合、タスクは2行のみで宣言されますが、その「本文」は個別に保存されます。







  - task: hello-world file: path/to/my_task.yml
      
      





runコマンドにも同じことが当てはまります。その内容はシェルスクリプトに入れて1つのコマンドで参照できるため、コードが短くなります。 たとえば、スクリプトをローカルでテストしたり、 単体テストでカバーしたい場合に便利です。 この場合、タスクは次のようになります。







 - task: "Run altogether" config: platform: linux image_resource: type: docker-image source: repository: somedocker/image tag: latest run: path: path/to/script.sh
      
      





スクリプトが標準のUNIX 出力結果を返すことを忘れないでください(0-すべてが正常であり、他の数値はエラーです)。 この方法でのみ、ConcourseCIはあなたのチームが成功したかどうかを知ることができます。







最後に、2番目のリソースについて簡単に説明します。これを-put:と宣言します。これは、ソースコードの場合のように、それからデータを取得しないことを意味します。







設定ファイルの準備ができたら、任意の名前(たとえば、 pipeline.yml



でファイルに保存し、次のコマンドでリモートConcourseCIサーバー上でパイプラインを作成/更新する必要があります。







$ fly -t office sp -c pipeline.yml -p my-pipeline-name









その後、ブラウザを開いて、作成(または更新)されたパイプラインを確認します。 gitリポジトリにコミットすると、自動的に開始されます:







ConcourseCIパレードの最初の起動







この大きな正方形(「プロジェクトのビルド」ジョブ)をクリックすると、詳細が表示されます。 画面上のこれらのステップはすべて「展開」して、コンソールの出力を表示できます。







ConcourseCIはタックス、詳細、タックスを開封します







秘密



私の例では、コード内で直接秘密(パスワード、キー)を保護していることがわかりました。 もちろん、実際のプロジェクトでは、セキュリティ上の考慮事項を考慮する価値はありません。YAMLファイルは一般的なリポジトリにあるからです。 すべての秘密は別々に保管する必要があります。 コンコースでは、パイプラインの更新時にのみ実際の値に置き換えられる特別なエイリアスを使用できます。 パスワードと秘密鍵を二重角括弧で囲まれたプレースホルダーに置き換えたとしましょう:







 - name: source-code type: git source: uri: git@your-project.git branch: master private_key: ((git-pivate-key)) - name: aws-s3-release type: s3 source: bucket: releases regexp: directory_on_s3/release-(.*).tgz access_key_id: ((aws-access-key)) secret_access_key: ((aws-secret-key))
      
      





そして、すべての値を含むYAML形式のパスワードとキー(たとえば、 credentials-ci.yml



)を使用して、ローカルファイル(コンピューターまたは他の安全なストレージにのみ安全に保存されます)を作成します。







 aws-access-key: "myawsaccesskey" aws-secret-key: "myawssecretkey" ftp-password: "my-secure-password" git-pivate-key: | -----BEGIN RSA PRIVATE KEY----- ............
      
      





そして、今度は--load-vars-from



オプションを使用して、 --load-vars-from



実際の値に置き換える必要があります。 したがって、完全なパイプラインの作成/更新コマンドは次のようになります。







fly -t office sp -c pipeline.yml -p my-pipeline-name --load-vars-from ~/credentials-ci.yml









これで、パイプラインは共通のリポジトリに保存され、パスワードは安全な場所にある管理者のコンピューターにのみ保存されます。 これで、少なくともパブリックgithubにビルドコンフィギュレーターを安全に残すことができます。







または、 標準のVaultツールのリソースを使用できます







なぜタスクが必要なのですか?



おそらく、私の例を見ると、なぜジョブとタスクが必要で、なぜそれらを単一の全体に結合できないのか疑問に思っています(たとえば、これはGitLabCI行われます )。 1つのタスクを小さなステップに分割する必要があるのはなぜですか? 良い質問です。例を挙げて説明します。







設計上、 ジョブは分離されたタスクであり、コンベアの他の部分に依存しません。 これは、いつでも起動できることを意味し、事前のアクションは必要ありません。 すべての中間および最終操作は内部で行われ、外部ではすべてが単一のステップのように見えます。 したがって、アセンブリ内に個別に存在できない操作がある場合、すべての依存ステップを1つのタスクに結合する必要があります。 これは、一方では困難になります。1つのタスクに集中しすぎるジェスチャが多すぎるため、もう一方には、プロセスを論理的なステップに分割する優れたツールがあります。タスクです。 さらに、各タスクは異なるツールを使用できます。







より明確にするために、より複雑な例を考えてみましょう。 前の段落で、たった1つのチーム( gradle build



組み立てられた単純なプロジェクトの例を挙げました。 しかし、これは常に起こるとは限りません。 古典的なモノリスの例を見てみましょう。フロントエンド、バックエンド、すべてすべてが同じリポジトリにある単一のプロジェクトです。 そのようなプロジェクトの組み立てはどうですか? すべての仮想ステップをリストします。









それで、私たちは何を数えますか?フルビルドを行うには、システムにnpm、gulp、git(sshに依存する)、go、govendorが必要です。覚えているように、ConcourseCIのアセンブリはdockerコンテナで実行されるため、ロジックは、カスタムdockerイメージを構築し、上記のすべてをそこに入れて、プライベートレジスタに配置するのが良いことを示しています。このイメージには必要なものがすべて含まれており、その中に必要なコマンドを実行できます-git、npm、およびgo はい、この方法は機能しますが、もちろんそうする価値はありません。そして、ここでは、1つのタスク(ジョブ)をさまざまな小さなドッカーコンテナーで起動される多くのサブタスクに分割すると便利です。そのため、次のようなタスクがあります。







  1. NPM依存関係をロードし、クライアント側を構築します。
  2. Git
  3. Go


, , . (job), -, .







, , . — NPM, Gulp , — git ssh , — go-vendor . , , , !







, , ? ! ConcourseCI aggregate , , , . , , , , , :







  ╭  NPM ,   Gulp build 1. ┨ Git version ╰  Go  2.   go build
      
      





, ( source-code docker-example-image ):







 ... jobs: - name: "Build monolith example" plan: - get: source-code trigger: true #     aggregate, #     - aggregate: # Build the frontend (SASS and Javascript) - task: "Build frontend, compile SASS, download dependencies" config: platform: linux image_resource: type: docker-image source: repository: 'monostream/nodejs-gulp-bower' inputs: - name: source-code outputs: - name: compiled-assets run: path: sh args: - -ec - | cd source-code/ #   npm install #   gulp build #     outputs, #       cp -r dist/* ../compiled-assets/ - task: "Download GO dependencies" config: platform: linux image_resource: type: docker-image source: repository: "electrotumbao/go-govendor" inputs: - name: source-code #    Go:    #     #      #     #   path path: src/authorName/repoName outputs: - name: vendor-output run: path: sh args: - -ec - | export GOPATH GOPATH="$(pwd)" cd src/authorName/repoName/ || exit #       output, #       govendor fetch -v +out cp -r vendor/* "$GOPATH"/vendor-output - task: "Update GIT commit details" config: platform: linux image_resource: type: docker-image source: { repository: 'alpine/git', tag: "latest"} inputs: - name: source-code outputs: - name: versioned-file run: path: sh args: - -ec - | #        cd source-code GITSHA=$(git rev-parse HEAD) GITAUTHOR=$(git log --format='%an %ae' -1) GITDATE=$(git log --format='%aD' -1) TODAY=$(date) #    -   , #     sed sed ... #       output, #       cp version.config ../versioned-file/ #  ,     #   ,     - task: "Build Source Code" config: platform: linux image_resource: type: docker-image source: {repository: "golang", tag: "alpine" } #  inputs    #       inputs: - name: source-code path: src/authorName/repoName - name: versioned-file - name: compiled-assets - name: vendor-output outputs: - name: output-for-docker run: path: sh args: - -ec - | set -e export GOPATH GOPATH="$(pwd)" cd src/daxi.re/cyprus-tours/ || exit #          cp -r "$GOPATH"/compiled-assets/* ./assets/ cp -r "$GOPATH"/vendor-output/* ./vendor #    go test # ,   CGO_ENABLED=0 GOOS=linux go build -a -o app . #   -    #           . #   ,     -. #       # docker-example-image (. )      #        output cp app "$GOPATH"/output-for-docker/ cp Dockerfile "$GOPATH"/output-for-docker/ #  ,   , #  -    #   , #       #  Dockerfile # (     ) - put: docker-example-image params: { build: output-for-docker } get_params: {rootfs: true}
      
      





, .







ConcourseCIコンベヤー







, , (job). , . , : NPM , — Go ( ). .







ConcourseCIでのコンカレントアセンブリプロセス







, , :







ConcourseCIはタスクを正常にアセンブルしました







出来上がり! , ! . , -.







, . . , ! !







:









UPD 15.3.2018: , , .








All Articles