今年の初めに、CI / CDプロセスをサポートするためのオープンソースユーティリティ(dappバージョン0.25)に十分な機能セットがあり、イノベーションの作業が開始されたと判断しました。 バージョン0.26では、YAML構文が登場し、Ruby DSLがクラシックとして宣言されました(その後、サポートされなくなります)。 次のバージョン0.27では、主な革新はAnsibleを備えたコレクターの外観と考えることができます。 これらの新製品についてさらに詳しく説明するときが来ました。
2019年8月13日更新: dappプロジェクトの名前が
werfに変更され、そのコードがGoに書き換えられ、ドキュメントが大幅に改善されました。
背景
私たちは2年以上
dappを開発しており、日常のメンテナンスでさまざまなサイズの多くのプロジェクトを積極的に使用しています。 ユーティリティの最初のバージョンは、
Chefを使用してイメージを構築することを目的として考案されました。 これに、
Rubyがほとんどすべてのエンジニアと開発者に馴染みがあるという事実を追加したとき、dappをRuby gemとして実装するという論理的な決定を下しました。 Dappfile構成をRuby DSLの形式で作成することが適切であると考えられました-近いフィールドであるVagrantからの成功例が知られているため、なおさらそうです。
ユーティリティが開発されると、dappには2番目の専門分野である
Kubernetesへのアプリケーション配信が必要であることが理解されるようになりました。 そのため
、Helmチャートで作業する
モードがあり、エンジニアはGoでYAML構文とテンプレートをマスターしましたが、開発者はHelmにパッチを送信し始めました。 一方では、Kubernetesへの配信はdappの不可欠な部分になり、他方では、GoはDockerおよびKubernetesエコシステムの事実上の標準になっています。 Rubyで書かれたdappは今では見えません:Dockerコードを再利用するのが難しい場合、ユーザーはしばしばアセンブリマシンにRubyを置きたくないだけです。バイナリをダウンロードする方がはるかに簡単で馴染みやすいです。その結果、dapp開発の主な目標は:a)コードベースのGoへの翻訳、b)YAML構文の実装。
さらに、過去数年間、シェフは機械の制御と組み立ての両方の理由で、私たちに合うのをやめてきました。
結局のところ、Ansibleに切り替えることで、DevOpsエンジニアだけでなく、会議で最も頻繁に発生する問題
はdappでのAnsibleのサポートでした 。 したがって、3番目の目標はAnsible-collectorの実装でした。
YAML構文
先ほど、
この記事ですでにYAML構文を紹介しましたが、今からさらに詳しく検討します。
アセンブリ構成は、
dappfile.yaml
(または
dappfile.yml
)で説明できます。 構成処理ステップは次のとおりです。
- dappは
dappfile.y[a]ml
読み取ります。 - Goテンプレートエンジンが起動し、最終的なYAMLがレンダリングされます。
- レンダリングされた設定はYAMLドキュメントに分割されます(
---
改行あり); - 各YAMLドキュメントに最上位にdimgまたはartifact属性が含まれていることが検証されます。
- 残りの属性の構成が確認されます。
- すべてが正常である場合-最終構成は、指定されたdimg'eyおよびartifact'ovから作成されます。
古典的なDappfileはRuby DSLであるため、環境変数の
ENV
辞書へのアクセス、ループ内のdimgの定義、コンテキスト継承を使用した一般的なビルド命令の定義など、いくつかのプログラミングが可能です。 開発者からそのような機会を奪わない
ために、Go-templatesのサポートをdappfile.ymlに追加
することが決定されました-Helm chart'amと同様です。
ただし、ネストとdimg_groupsを使用してコンテキストの継承を放棄しました。 これは、利便性よりも混乱をもたらしました。 したがって、
dappfile.yml
はYAMLドキュメントの線形配列であり、各ドキュメントはdimgまたは成果物の説明です。
前と同じように、dimgは1でも名前なしでもかまいません。
dimg: ~ from: alpine:latest shell: beforeInstall: - apk update
アーティファクトには名前が必要です 現在では、アーティファクトイメージからのファイルのエクスポートではなく、インポート(Dockerfileからのマルチステージ機能と同様)について説明しています。 したがって、ファイルを取得するアーティファクトを指定する必要があります。
artifact: application-assets ... --- dimg: ~ ... import: - artifact: application-assets add: /app/public/assets after: install - artifact: application-assets add: /vendor to: /app/vendor after: install
ディレクティブ
git
、
git remote
、
shell
はDSLからYAMLにほとんど「そのまま」切り替えられますが、アンダースコアの代わりにcamelCaseが使用され(Kubernetesのように)、ディレクティブを繰り返す必要はありませんが、配列を指定してパラメーターを結合する必要があります:
git: - add: / to: /app owner: app group: app excludePaths: - public/assets - vendor - .helm stageDependencies: install: - package.json - Bowerfile - Gemfile.lock - app/assets/* - url: https://github.com/kr/beanstalkd.git add: / to: /build shell: beforeInstall: - useradd -d /app -u 7000 -s /bin/bash app - rm -rf /usr/share/doc/* /usr/share/man/* - apt-get update - apt-get -y install apt-transport-https git curl gettext-base locales tzdata setup: - locale-gen en_US.UTF-8
利用可能なすべての属性の基本的な説明は、
ドキュメントに記載されてい
ます 。
ドッカーENVおよびLABEL
dappfile.yml
では、環境変数とラベルを次のように追加できます。
docker: ENV: <key>: <value> ... LABELS: <key>: <value> ...
YAMLでは、DappfileおよびDockerfileにあったように、
ENV
または
LABELS
を繰り返すことはできません。
テンプレートエンジン
テンプレートを使用して、異なるdimgまたはアーティファクトの全体的なビルド構成を決定できます。 これは、たとえば、変数を使用して共通のベースイメージを簡単に示すことができます。
{{ $base_image := "alpine:3.6" }} dimg: app from: {{ $base_image }} ... --- dimg: worker from: {{ $base_image }}
...または定義されたパターンを使用したより複雑なもの:
{{ $base_image := "alpine:3.6" }} {{- define "base beforeInstall" }} - apt: name=php update_cache=yes - get_url: url: https://getcomposer.org/download/1.5.6/composer.phar dest: /usr/local/bin/composer mode: 0755 {{- end}} dimg: app from: {{ $base_image }} ansible: beforeInstall: {{- include "base beforeInstall" .}} - user: name: app uid: 48 ... --- dimg: worker from: {{ $base_image }} ansible: beforeInstall: {{- include "base beforeInstall" .}} ...
この例では、
beforeInstall
ステージの指示の一部が共通部分として定義されており、各dimgにさらに含まれています。
Go /テンプレートの機能の詳細については、テキスト/テンプレートモジュールの
ドキュメントと、標準機能を補完するsprigモジュールの
ドキュメントを参照してください。
Ansibleのサポート
Ansible-collectorは3つの部分で構成されています。
- Python 2.7が置かれているdappdeps / ansibleイメージ。glibcおよびその他のライブラリでコンパイルされ、あらゆるディストリビューションで動作します(特にAlpineに関連)。 Ansibleはすぐそこにインストールされます。
dappfile.yaml
Ansibleを使用したdappfile.yaml
アセンブリ構文のサポート 。- ステージ用のコンテナーを起動するdappのビルダー 。 これらのコンテナでは、
dappfile.yml
指定されたタスクdappfile.yml
ます。 Builderはプレイブックを作成し、それを起動するコマンドを生成します。
Ansibleは多数のリモートホストの管理システムとして開発されているため、開発者はローカルスタートアップに関連するものを無視できます。 たとえば、Chefの場合のように、実行中のコマンドからの
リアルタイム出力はありません 。アセンブリには、実行時間の長いコマンドが含まれている場合があります。その出力はリアルタイムで表示すると便利ですが、Ansibleは完了後にのみ出力を表示します。 GitLab CIを介して起動した場合、これはビルドのハングと見なすことができます。
2番目の問題は、Ansibleの一部である
stdoutコールバックでした。 それらの中には「適度に有益」ではありませんでした。 JSON形式の完全な結果を含む冗長な出力、またはホスト名、モジュール名、およびステータスを含むミニマリズムがあります。 もちろん、私は誇張していますが、画像を組み立てるための適切なモジュールは本当にありません。
3番目に遭遇したのは、いくつかのAnsibleモジュールが外部ユーティリティ(怖くない)、Pythonモジュール(さらに怖くない)、および
Pythonバイナリモジュール (悪夢!)に依存していることです。 繰り返しますが、Ansibleの作成者は、作成がシステムバイナリとは別に起動されること、たとえば、
userdel
が
/sbin
ではなく、別のディレクトリのどこかにあることを考慮しませんでした...
バイナリモジュールの問題は、aptモジュールの機能です。 SOライブラリとしてpython-aptモジュールを使用します。 aptモジュールの別の機能は、タスクの実行中に、python-aptのロードが失敗した場合に、このモジュールを含むパッケージをシステムにインストールしようとすることです。
上記の問題を解決するために、生およびスクリプトタスク用の「ライブ」出力
が実装されました。 Ansiballzメカニズムなしで起動できます。 また、stdoutコールバックを実装し、
useradd
、
userdel
、
usermod
、
getent
などのユーティリティアセンブリをdappdeps / ansibleに追加し、python-aptモジュールをコピーする必要がありました。
その結果、dappのAnsibleビルダーはUbuntu、Debian、CentOS、AlpineのLinuxディストリビューションで動作し
ますが、すべての
モジュールがまだテストされているわけではないため、dappには正確にサポートされるモジュールのリストがあります。 構成でリストにないモジュールを使用する場合、アセンブリは開始されません-これは一時的な措置です。 サポートされているモジュールのリストは
ここにあります 。
dappfile.yml
でAnsibleを使用したビルド構成は、
shell
構成に似ています。
ansible
キーには必要なステージがリストされ、各ステージにはタスクの配列が定義されています-通常のプレイブックとほぼ同様に、
tasks
属性の代わりにステージ名のみが示されます。
ansible: beforeInstall: - name: "Create non-root main application user" user: name: app comment: "Non-root main application user" uid: 7000 shell: /bin/bash home: /app - name: "Disable docs and man files installation in dpkg" copy: content: | path-exclude=/usr/share/man/* path-exclude=/usr/share/doc/* dest: /etc/dpkg/dpkg.cfg.d/01_nodoc install: - name: "Precompile assets" shell: | set -e export RAILS_ENV=production source /etc/profile.d/rvm.sh cd /app bundle exec rake assets:precompile args: executable: /bin/bash
例は
ドキュメントから取得され
ます 。
ここで疑問が生じます:
dappfile.yml
にタスクのリストしかない場合、他のすべて(トップレベルのプレイブック、インベントリ)がどこにあるのか、話をする牛を有効にする方法と話をしている牛はどこにいるのか(または無効にする方法)? Ansibleの起動方法について説明します。
ビルダーは
起動を担当します -これは、ステージでDockerコンテナーの起動パラメーターを決定する非常に複雑なコードではありません:環境変数、ansible-playbook起動コマンド、必要なマウント。 また、ビルダーは、いくつかのファイルが生成されるアプリケーションの一時ディレクトリにディレクトリを作成します。
hosts
-Ansibleのインベントリ。 マウントされたイメージdappdeps / ansible内のPythonへのパスを持つlocalhostホストは1つだけです。ansible.cfg
-Ansible設定。 構成では、接続タイプはlocal
、インベントリへのパス、コールバックstdoutへのパス、一時ディレクトリへのパス、および設定become
次のとおりです。すべてのタスクはrootユーザーから起動されます。 become_user
を使用すると、すべての環境変数がユーザープロセスで使用可能になり、 $HOME
( sudo -E -H
)が正しく設定されます。playbook.yml
このファイルは、実行中のステージのタスクのリストから生成されます。 hosts: all
フィルターはhosts: all
指定され、暗黙的なファクトコレクションはgather_facts: no
設定することにより無効にされます。 setupおよびset_factモジュールはサポートされるモジュールのリストにあるため、それらを使用してファクトを明示的に収集できます。
前の例の
beforeInstall
ステージのタスクのリストは、このような
playbook.yml
変わります。
--- hosts: all gather_facts: no tasks: - name: "Create non-root main application user" user: name: app ... - name: "Disable docs and man files installation in dpkg" copy: content: | path-exclude=/usr/share/man/* path-exclude=/usr/share/doc/* dest: /etc/dpkg/dpkg.cfg.d/01_nodoc
Ansible Assemblyアプリケーションの機能
なる
ansible.cfg
設定は次のとおりです。
[become] become = yes become_method = sudo become_flags = -E -H become_exe = path_to_sudo_insdie_dappdeps/ansible_image
したがって、タスクでは、
become_user: username
のみを指定してスクリプトを実行するか、ユーザーからコピーします。
コマンドモジュール
Ansibleには、コマンドおよびスクリプトを実行するための4つのモジュール(
raw
、
script
、
shell
および
command
ます。
raw
と
script
は、Ansiballzメカニズムなしで実行されます。これはわずかに高速であり、ライブ出力があります。
raw
複数行のアドホックスクリプトを実行できます。
- raw: | mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests
確かに、
environment
属性はサポートされていませんが、これを回避できます:
- raw: | mvn -B -f pom.xml -s $SETTINGS dependency:resolve mvn -B -s $SETTINGS package -DskipTests args: executable: SETTINGS=/usr/share/maven/ref/settings-docker.xml /bin/ash -e
ファイル
この段階では、
git
ディレクティブを除き、リポジトリからコンテナにファイルを転送するメカニズムはありません。 さまざまな種類の構成、スクリプト、その他の小さなファイルをイメージに追加するには、コピーモジュールを使用できます。
- name: "Disable docs and man files installation in dpkg" copy: content: | path-exclude=/usr/share/man/* path-exclude=/usr/share/doc/* dest: /etc/dpkg/dpkg.cfg.d/01_nodoc
ファイルが大きい場合、
dappfile.yml
内に保存しないために、Go-templateおよび
.Files.Get
関数を使用できます。
- name: "Disable docs and man files installation in dpkg" copy: content: | {{.Files.Get ".dappfiles/01_nodoc" | indent 6}} dest: /etc/dpkg/dpkg.cfg.d/01_nodoc
将来的には、ファイルをアセンブリコンテナに接続するためのメカニズムが実装され、大きな
include*
およびバイナリファイルをコピーしやすくなり、
include*
または
import*
を使用できるようになり
include*
。
テンプレート化
dappfile.yaml
Goテンプレートについては既に述べました。 Ansibleはjinja2テンプレートをサポートしており、これら2つのシステムのセパレーターは同じであるため、jinja呼び出しはGo-templateエンジンからエスケープする必要があります。
- name: "create temp file for archive" tempfile: state: directory register: tmpdir - name: Download archive get_url: url: https://cdn.example.com/files/archive.tgz dest: '{{`{{ tmpdir.path }}`}}/archive.tgz'
ビルドの問題をデバッグする
タスクを実行すると、何らかのエラーが発生する場合がありますが、画面上のメッセージだけでは理解できない場合があります。 この場合、環境変数
ANSIBLE_ARGS="-vvv"
指定して開始できます。出力には、タスクのすべての引数と結果のすべての引数があります(json stdoutコールバックの使用と同様)。
状況が明確でない場合は、イントロ
dapp dimg bulid --introspect-error
モードでアセンブリを開始できます:
dapp dimg bulid --introspect-error
エラーが発生するとアセンブリが停止し、コンテナ内でシェルが起動します。 エラーの原因となったコマンドが表示され、隣接するターミナルで一時ディレクトリに移動して
playbook.yml
を編集できます。

ゴーゴーゴー
これはdappの開発における3番目の目標ですが、ユーザーの観点からは、インストールの簡素化を除いて、ほとんど変更はありません。 Goのリリース0.26では、
dappfile.yaml
パーサーが実装されました。 現在、メインのdapp機能をGoに変換する作業が進行中です。アセンブリコンテナ、ビルダーの起動、Gitとの連携。 したがって、Ansibleモジュールを含むテストでのあなたの助けは不要ではありません。
GitHubで問題が発生するのを待っ
ているか、Telegramのグループ
dapp_ruにアクセスしてください 。
2019年8月13日更新: dappプロジェクトの名前が
werfに変更され、そのコードがGoに書き換えられ、ドキュメントが大幅に改善されました。
PS
牛はどうですか? cowsayプログラムはdappdeps / ansibleではなく、使用されるコールバックstdoutはcowsayが含まれるメソッドを呼び出しません。 残念ながら、牛のいないdappでのAnsible(しかし、誰もあなたが問題を作成するのを止めないでしょう)。
PPS
ブログもご覧ください。