1台のマシン(Docker、Ansible、TeamCity)に独立した開発環境の自動展開をセットアップする

この投稿では、TheQuestionでの長年の夢である、個々のタスクごとに個別に自動的に展開される開発環境をどのように実現したかを説明します。







画像//画像







当初から、開発は次のように構築されていました。









master



ブランチのCIは、非常に通常の方法で配置されていると言う価値があります。







  1. githubにプッシュ
  2. TeamCityは新しいコミットを確認して作成します
  3. 自動テストの実行
  4. Dockerコンテナーが予定されています
  5. Ansibleはコンテナをデプロイします


あまり変わらないように、このシーケンスとツールを維持したかったのです。







1つの開発者の明らかな欠点は、一度に1つのブランチしか見ることができず、未完了のタスクが相互に干渉し、一定の競合を解決する必要があることです。 私たちの目標は、GitHubで新しいブランチが作成されるとすぐに、別の開発者がGitHubで作成されることでした。







一見、タスクは難しくありません-クラウドプラットフォームのAPIを確認し、新しいブランチでの最初のコミットが開始される前に、このブランチ用に別のサーバーを作成します-シンプルで、すでに1台のマシンでスキャンが行われています、Ansible!







しかし、重要な問題が1つあります。データベースです。 控えめなマシンでの圧縮ダンプ(まだダウンロードする必要があります)からの完全スキャンには、約2時間かかります。 もちろん、すべてをより効率的なマシンにデプロイするか待つだけでかまいませんが、クラウドAPIを使用してすべてを書き直したくはありません(別の場所に移動するときにすべてを書き直さなければならないという事実にもかかわらず)。 したがって、このソリューションでは、1台の平均的なマシンを使用します。







Teamcity







これはほとんどカスタマイズする必要のない素晴らしいツールです。 彼に必要なことは、どのブランチを使用しているかをスクリプトに伝えることだけです。

だから、唯一のBuild Step: command line



変更Build Step: command line



から







 cd clusters/dev make
      
      





になった







 export branch_name=%teamcity.build.branch% cd clusters/dev make
      
      





Docker







乙女が1人いると、インフラストラクチャの各部分は、アプリケーションアプリケーションの一部であるかどうかに関係なく、Sphinx、Redis、Nginx、またはPostgreSQLが別々のコンテナ内で起動されました。 --network-mode=host



指示で開始されました。つまりip:port



コンテナの各ip:port



ホストマシンのlocalhost:port



と一致していました。







ご理解のとおり、これは複数のバージンでは機能しません。まず、コンテナは1つのブランチのコンテナとのみ通信する必要があり、次に、 nginx



は必要な各コンテナの内部IPを知っている必要があります。







次に、 Docker network



が助けになり、コンテナの起動が







 docker run /path/to/Dockerfile
      
      











 docker network create ${branch_name} --opt com.docker.network.bridge.name=${branch_name} docker run --network=${branch_name} -e branch_name=${branch_name} /path/to/Dockerfile
      
      





これにより、次のことがわかります。









PostgreSQL







前と同様に--network=host



を指定してコンテナで実行します。これにより、DBMSは1つになりますが、ブランチごとに-独自のユーザーと独自のデータベースがあります。







新しいデータベースをすばやく展開するタスクは、テンプレートによって完全に解決されます。







 CREATE DATABASE db_name TEMPLATE template_name
      
      





加えて、毎日販売のデータベースの新しいコピーを持ちたいので、ブランチを作成するときにそれを信頼します( --network=host



別のコンテナに流れ--network=host









これを行うには、2つのベースを作成します。 毎晩、新しいダンプを1つに展開するのに2時間かかります。







 pg_restore -v -Fc -c -d template_new dump_today.dump
      
      





そして成功した場合:







 DROP template_today; CREATE DATABASE template_today TEMPLATE template_new;
      
      





その結果、毎朝新しいテンプレートがあり、次のダンプが破られて展開に失敗した場合でも残ります。







新しいブランチを作成するとき、テンプレートからベースを作成します







 CREATE USER db_${branch_name}; CREATE DATABASE db_${branch_name} OWNER db_${branch_name} TEMPLATE template_today;
      
      





したがって、ブランチ用に個別のデータベースを作成するには2時間ではなく20分かかり、Dockerコンテナ内からの接続は、常にホストマシンIPを指すeth0



インターフェイスを介して行われます。







nginx







また、ホストマシンにインストールし、 docker inspect



を使用して構成を収集します。このコマンドは、コンテナに関する完全な情報を提供します。そこから、構成テンプレートで置き換えるIPアドレスが必要になります。







また、ネットワークインターフェイスの名前がブランチの名前と一致するため、1つのスクリプトですべてのバージンの構成を一度に生成できます。







 for network in $(ip -o -4 as | awk '{ print $2 }' | cut -d/ -f1); do if [ "${network}" == "eth0" ] || [ "${network}" == "lo" ] || [ "${network}" == "docker0" ]; then continue fi IP=$(docker inspect -f "{{.NetworkSettings.Networks.${network}.IPAddress}}" ${container_name}) sed -i "s/{{ ip }}/${IP}/g" ${nginx_conf_path} sed -i "s/{{ branch_name }}/${network}.site.url/g" ${nginx_conf_path} done
      
      





ブランチを削除する







master



を除く各ブランチの寿命が短いため、ブランチに関連するすべてのもの(nginx config、containers、base)を定期的に削除する必要があります。







残念ながら、ブランチが削除されたことをTeamCityに伝える方法を見つけることができなかったので、私は考えなければなりませんでした。







次のブランチがデプロイされると、その名前のファイルがマシン上で呼び出されます:







 touch /branches/${branch_name}
      
      





これにより、所有しているすべてのブランチだけでなく、最後の変更の時刻(ファイルが変更された時刻と一致する)も思い出すことができます。 ブランチをすぐに削除するのではなく、使用を停止してから1週間後に削除すると非常に便利です。 次のようになります。







 #!/usr/bin/env bash MAX_BRANCH_AGE=7 branches_to_delete=() for branch in $(find /branches -maxdepth 1 -mtime +${MAX_BRANCH_AGE}); do branch=$(basename ${branch}) if [ ${branch} == "master" ]; then continue fi branches_to_delete+=(${branch}) done dbs=() for db in $(docker exec -it postgresql gosu postgres psql -c "select datname from pg_database" | \ grep db_ | \ cut -d'_' -f 2); do dbs+=(${db}) done for branch in ${branches_to_delete[@]}; do for db in ${dbs[@]}; do if [ ${branch} != ${db} ]; then continue fi # branch file rm /branches/${branch} # nginx rm /etc/nginx/sites-enabled/${branch} # containers docker rm -f $(docker ps -a | grep ${branch}- | awk '{ print $1 }') # db docker exec -i postgresql gosu postgres psql <<-EOSQL DROP USER db_${branch}; DROP DATABASE db_${branch}; EOSQL done done service nginx reload
      
      





いくつかの落とし穴







すべてが機能し、 master



をつかまえるとすぐに-彼は準備ができませんでした。 master



iproute2



master



キーワードであることが判明したため、 ifconfig



を一緒に使用してIPコンテナーを定義しました。







だった:







 ip -o -4 as ${branch_name} | awk '{ print $2 }' | cut -d/ -f1
      
      





になりました:







 ifconfig ${branch_name} | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'
      
      





thq-1308



ブランチが( Jira



からのタスク番号によって)作成されるとすぐに、アセンブルされませんでした。 そしてすべてはダッシュのためです。 いくつかの場所で邪魔になります:PostgreSQLとDocker Inspect出力テンプレート。

その結果、ホストIPがわかります。







 docker inspect -f "{{.NetworkSettings.IPAddress}}" ${network}-theq
      
      





新しいデータベースのすべてのテーブルの所有者を変更します。







 tables=`gosu postgres psql -h ${DB_HOST} -qAt -c "SELECT tablename FROM pg_tables WHERE schemaname = 'public';" "${DB_NAME}"` for tbl in $tables ; do gosu postgres psql -h ${DB_HOST} -d "${DB_NAME}" <<-EOSQL ALTER TABLE $tbl OWNER TO "${DB_USER}"; EOSQL done
      
      





一般に、それですべてです。 完全なコマンド、スクリプト(最後のコマンドを除く)、ansibleの役割は与えませんでした-特別なことは何もありませんが、その点を見逃していないことを願っています。 コメントのすべての質問に答える準備ができています。








All Articles