2020幎にデヌタベヌスの倉曎を開発、デバッグ、提䟛するプロセスをどのように発明したか

䞭庭は2020幎で、「Kubernetesが答えです」、「Microservices」、「Service mesh」、「Sesuritiポリシヌ」などのバックグラりンドノむズを聞くこずに慣れおいたす。呚りの誰もが明るい未来に向かっお走っおいたす。



圓瀟は、アプリケヌションよりもデヌタベヌスに関しお、より保守的なアプロヌチを採甚しおいたす。デヌタベヌスはKubernetesではなく、ハヌドりェアたたは仮想マシンで回転しおいたす。支払い凊理デヌタベヌスを倉曎するための確立されたプロセスがありたす。これには、倚くの自動チェック、倧芏暡なレビュヌ、およびDBAの参加によるリリヌスが含たれたす。この堎合のチェックの数ず関係者は、垂堎投入たでの時間に悪圱響を及がしたす。䞀方、それはデバッグされおおり、本番環境に確実に倉曎を加えるこずができるため、䜕かが壊れる可胜性を最小限に抑えるこずができたす。そしお、䜕かが壊れた堎合、適切な人がすでに修理プロセスに含たれおいたす。このアプロヌチにより、䌚瀟のメむンサヌビスの䜜業がより安定したす。



PostgreSQL䞊のマむクロサヌビス甚の新しいリレヌショナルデヌタベヌスのほずんどを開始したす。Oracleのデバッグされたプロセスは、信頌性はありたすが、小さなデヌタベヌスでは䞍必芁に耇雑になりたす。困難なプロセスを過去から明るい未来に匕きずり蟌みたいず思う人は誰もいたせん。誰も前もっお明るい未来のためのプロセスに取り組み始めたせんでした。その結果、暙準ずraznozhopitsuが䞍足したした。 これがどのような問題を匕き起こし、どのように解決したかを知りたい堎合は、catぞようこそ。











私たちが解決した問題



統䞀されたバヌゞョン管理基準はありたせん



最良の堎合、これらは、マむクロサヌビスのあるリポゞトリのdbディレクトリのどこかにあるDDLSQLファむルです。これがデヌタベヌスの珟圚の状態であり、テストず本番で異なり、デヌタベヌススキヌマの参照スクリプトがない堎合は非垞に悪いです。



デバッグ䞭に、テストベヌスを粉砕したす



「私は今、テストデヌタベヌスを少し揺さぶっおいたす、そこで心配しないでください」-そしおテストデヌタベヌスに新しく曞かれたスキヌマ倉曎コヌドをデバッグしに行きたした。時間がかかる堎合がありたすが、その間、テスト回路は機胜したせん。



同時に、他のマむクロサヌビスがマむクロサヌビスず盞互䜜甚する郚分でテスト回路が砎損する可胜性がありたす。マむクロサヌビスのベヌスは開発者が砎壊したした。



DAOメ゜ッドはテストの察象倖であり、CIで怜蚌されおいたせん



開発およびデバッグ時に、DAOメ゜ッドは、倖偎のハンドルを数局䞊に匕くこずによっお呌び出されたす。これにより、マむクロサヌビスずデヌタベヌス間の特定の盞互䜜甚ではなく、ビゞネスロゞックのシナリオ党䜓が公開されたす。



将来䜕も厩壊しないずいう保蚌はありたせん。マむクロサヌビスの品質ず保守性が䜎䞋したす。



メディアの非同圢性



倉曎ルヌプがテストず本番環境で異なる方法で配信される堎合、同じように機胜するかどうかはわかりたせん。特に、開発ずデバッグが実際にテストで実行される堎合。



テスト䞊のオブゞェクトは、開発者たたはアプリケヌションのアカりントで䜜成できたす。助成金はランダムに枡され、通垞はすべおの特暩を付䞎したす。アプリケヌションの助成金は、「ログに゚ラヌが衚瀺されたす-助成金を出したす」ずいう原則に基づいお発行されたす。助成金はリリヌス時に忘れられるこずがよくありたす。リリヌス埌、smokテストがすべおの新機胜をカバヌしおおらず、助成金の欠劂がすぐに発生しない堎合がありたす。



生産に転がる重くお壊れやすいプロセス



本番環境ぞのロヌルむンは手動で行われたしたが、Oracleのプロセスず同様に、DBA、リリヌスマネヌゞャヌの承認、リリヌス゚ンゞニアによるロヌルフォワヌドが行われたした。



これにより、リリヌスが遅くなりたす。たた、問題が発生した堎合、ダりンタむムが増加し、開発者のデヌタベヌスぞのアクセスが耇雑になりたす。Oracle以倖のパッチ蚭定暙準がなく、テストが最埌たで行われおいたため、exec.sqlスクリプトずrollback.sqlスクリプトはテストでテストされないこずがよくありたした。



したがっお、開発者は、このプロセスをたったく行わずに、重芁ではないサヌビスに倉曎を加えるこずがありたす。



どうすれば良くなるこずができたすか



Dockerコンテナ内のロヌカルDBでのデバッグ



䞀郚の人にずっおは、この蚘事で説明されおいるすべおの技術的解決策は明癜に思えるかもしれたせん。しかし、どういうわけか、毎幎、同じレヌキを熱心に螏む人がいたす。



アプリケヌションコヌドを蚘述しおデバッグするためにssh経由でテストサヌバヌにアクセスするこずはありたせんかテストDBむンスタンスでデヌタベヌスコヌドを開発およびデバッグするのはばかげおいるず思いたす。䟋倖はありたすが、デヌタベヌスをロヌカルで立ち䞊げるこずは非垞に難しい堎合がありたす。しかし、通垞、軜量でレガシヌではないものに぀いお話しおいる堎合、ロヌカルでベヌスを䞊げお、すべおの移行を䞀貫しおロヌルするこずは難しくありたせん。その芋返りずしお、他の開発者によっお行き詰たるこずなく、アクセスが倱われるこずなく、開発に必芁な暩限を持぀安定したむンスタンスを自分の偎で取埗できたす。



ロヌカルデヌタベヌスを簡単に起動できる䟋を次に瀺したす



。2行のDockerfileを䜜成しおみたしょう。



FROM postgres:12.3
ADD init.sql /docker-entrypoint-initdb.d/


init.sqlで、「クリヌンな」デヌタベヌスを䜜成したす。これは、テストず本番の䞡方で取埗する予定です。次のものが含たれおいる必芁がありたす。



  • スキヌマの所有者ずスキヌマ自䜓。
  • スキヌマを䜿甚する暩限を持぀アプリケヌションナヌザヌ。
  • 必芁な拡匵機胜


Init.sqlの䟋
create role my_awesome_service
with login password *** NOSUPERUSER inherit CREATEDB CREATEROLE NOREPLICATION;
create tablespace my_awesome_service owner my_awesome_service location '/u01/postgres/my_awesome_service_data';
create schema my_awesome_service authorization my_awesome_service;
grant all on schema my_awesome_service to my_awesome_service;
grant usage on schema my_awesome_service to my_awesome_service;
alter role my_awesome_service set search_path to my_awesome_service,pg_catalog, public;

create user my_awesome_service_app with LOGIN password *** NOSUPERUSER inherit NOREPLICATION;
grant usage on schema my_awesome_service to my_awesome_service_app;

create extension if not exists "uuid-ossp";




䟿宜䞊、dbタスクをMakefileに远加できたす。これにより、コンテナヌがベヌスで再開始され、接続甚にポヌトが突き出たす。



db:
    docker container rm -f my_awesome_service_db || true
    docker build -t my_awesome_service_db docker/db/.
    docker run -d --name my_awesome_service_db -p 5433:5432 my_awesome_service_db


業界暙準のバヌゞョン倉曎セット



たた、明らかなように芋えたす。移行を蚘述し、バヌゞョン制埡システムに保持する必芁がありたす。しかし、バむンディングのない「裞の」sqlスクリプトをよく目にしたす。そしおこれは、誰が、䜕を、い぀ポンプしたかずいうロヌルバックずロヌルバックを制埡できないこずを意味したす。構造が倉曎されおいる可胜性があるため、SQLスクリプトをテストおよび本番デヌタベヌスで実行できるずいう保蚌すらありたせん。



䞀般的に、あなたはコントロヌルが必芁です。移行システムはほが制埡です。

異なるデヌタベヌススキヌマバヌゞョン管理システムの比范に぀いおは説明したせん。FlyWay vsLiquibaseはこの蚘事のトピックではありたせん。Liquibaseを遞びたした。



私たちのバヌゞョン



  • DDL-デヌタベヌスオブゞェクトの構造テヌブルの䜜成。
  • ルックアップテヌブルのDMLコンテンツ挿入、曎新。
  • UZアプリケヌションのDCL蚱可蚱可の遞択、挿入...。


ロヌカルデヌタベヌスでマむクロサヌビスを起動しおデバッグする堎合、開発者は助成金を凊理する必芁に盎面したす。そのための唯䞀の合法的な方法は、DCLスクリプトを倉曎セットに远加するこずです。これにより、助成金が確実に販売されたす。



sqlパッチセットの䟋
0_ddl.sql:

create table my_awesome_service.ref_customer_type
(
    customer_type_code    	varchar not null,
    customer_type_description varchar not null,
    constraint ref_customer_type_pk primary key (customer_type_code)
);
 
alter table my_awesome_service.ref_customer_type
    add constraint customer_type_code_ck check ( (customer_type_code)::text = upper((customer_type_code)::text) );


1_dcl.sql:



grant select on all tables in schema my_awesome_service to ru_svc_qw_my_awesome_service_app;
grant insert, update on my_awesome_service.some_entity to ru_svc_qw_my_awesome_service_app;


2_dml_refs.sql:



insert into my_awesome_service.ref_customer_type (customer_type_code, customer_type_description)
values ('INDIVIDUAL', '. ');
insert into my_awesome_service.ref_customer_type (customer_type_code, customer_type_description)
values ('LEGAL_ENTITY', '. ');
insert into my_awesome_service.ref_customer_type (customer_type_code, customer_type_description)
values ('FOREIGN_AGENCY', ' . ');


Fixtures. dev

3_dml_dev.sql:



insert into my_awesome_service.some_entity_state (state_type_code, state_data, some_entity_id)
values ('BINDING_IN_PROGRESS', '{}', 1);


rollback.sql:



drop table my_awesome_service.ref_customer_type;




Changeset.yamlの䟋
databaseChangeLog:
 - changeSet:
     id: 1
     author: "mr.awesome"
     changes:
       - sqlFile:
           path: db/changesets/001_init/0_ddl.sql
       - sqlFile:
           path: db/changesets/001_init/1_dcl.sql
       - sqlFile:
           path: db/changesets/001_init/2_dml_refs.sql
     rollback:
       sqlFile:
         path: db/changesets/001_init/rollback.sql
 - changeSet:
     id: 2
     author: "mr.awesome"
     context: dev
     changes:
       - sqlFile:
           path: db/changesets/001_init/3_dml_dev.sql




Liquibaseは、デヌタベヌス䞊にdatabasechangelogテヌブルを䜜成したす。このテヌブルには、ポンプアップされたチェンゞセットが蚘録されたす。

デヌタベヌスにロヌルする必芁がある倉曎セットの数を自動的に蚈算したす。



デヌタベヌスにロヌルむンする必芁があるいく぀かのチェンゞセットからスクリプトを生成する機胜を備えたmavenおよびgradleプラグむンがありたす。



デヌタベヌス移行システムのアプリケヌション起動フェヌズぞの統合



これは、移行制埡システムずアプリケヌションが構築されおいるフレヌムワヌクの任意のアダプタヌである可胜性がありたす。倚くのフレヌムワヌクがあり、ORMにバンドルされおいたす。たずえば、Ruby-On-Rails、Yii2、Nest.JS。



このメカニズムは、アプリケヌションコンテキストの開始時に移行をロヌルするために必芁です。

䟋えば



  1. テストデヌタベヌスでは、パッチセット001、002、003。
  2. pogromistはパッチセット004、005を開発し、アプリケヌションをテストにデプロむしたせんでした。
  3. テストにデプロむしたす。パッチセット004、005が展開されおいたす。


それらが回転しない堎合、アプリケヌションは起動したせん。ロヌリングアップデヌトは叀いポッドを殺したせん。



スタックはJVM + Springであり、ORMを䜿甚しおいたせん。したがっお、SpringずLiquibaseの統合が必芁でした。



圓瀟には重芁なセキュリティ芁件がありたす。アプリケヌションのナヌザヌは、限られた䞀連の蚱可を持っおいる必芁があり、スキヌマ所有者レベルのアクセス暩を持っおはなりたせん。Spring-Liquibaseを䜿甚するず、スキヌマ所有者ナヌザヌに代わっお移行をロヌルするこずができたす。この堎合、アプリケヌションアプリケヌションレベルの接続プヌルはLiquibaseDataSourceにアクセスできたせん。したがっお、アプリケヌションはスキヌマ所有者ナヌザヌからアクセスを取埗したせん。



Application-testing.yamlの䟋
spring:
  liquibase:
    enabled: true
    database-change-log-lock-table: "databasechangeloglock"
    database-change-log-table: "databasechangelog"
    user: ${secret.liquibase.user:}
    password: ${secret.liquibase.password:}
    url: "jdbc:postgresql://my.test.db:5432/my_awesome_service?currentSchema=my_awesome_service"




CIステヌゞでのDAOテストは怜蚌したす



私たちの䌚瀟にはそのようなCIステヌゞがありたす-確認しおください。この段階で、倉曎が内郚品質基準に準拠しおいるかどうかがチェックされたす。マむクロサヌビスの堎合、これは通垞、コヌドスタむルをチェックするためのリンタヌ実行であり、バグ、ナニットテストの実行、およびコンテキストホむストを䜿甚したアプリケヌションの起動です。これで、怜蚌段階で、デヌタベヌスの移行ず、アプリケヌションDAOレむダヌずデヌタベヌスの盞互䜜甚を確認できたす。



デヌタベヌスを䜿甚しおコンテナを起動し、パッチセットをロヌリングするず、䜜業䞭のマシンの胜力ずパッチセットの数に応じお、Springコンテキストの開始時間が1.5〜10秒長くなりたす。



これらは実際にはナニットテストではなく、アプリケヌションのDAOレむダヌをデヌタベヌスに統合するためのテストです。

デヌタベヌスをマむクロサヌビスの䞀郚ず呌ぶこずで、1぀のマむクロサヌビスの2぀の郚分の統合をテストしおいるず蚀えたす。倖郚䟝存関係はありたせん。したがっお、これらのテストは安定しおおり、怜蚌フェヌズで実行できたす。マむクロサヌビスずデヌタベヌスの契玄を修正し、将来の改善に安心を提䟛したす。



これは、DAOをデバッグするための䟿利な方法でもありたす。RestControllerを呌び出しお、䞀郚のビゞネスシナリオでナヌザヌの動䜜をシミュレヌトする代わりに、必芁な匕数を䜿甚しおDAOをすぐに呌び出したす。



サンプルDAOテスト
@Test
@Transactional
@Rollback
fun `create cheque positive flow`() {
      jdbcTemplate.update(
       "insert into my_awesome_service.some_entity(inn, registration_source_code)" +
               "values (:inn, 'QIWICOM') returning some_entity_id",
       MapSqlParameterSource().addValue("inn", "526317984689")
   )
   val insertedCheque = chequeDao.addCheque(cheque)
   val resultCheque = jdbcTemplate.queryForObject(
       "select cheque_id from my_awesome_service.cheque " +
               "order by cheque_id desc limit 1", MapSqlParameterSource(), Long::class.java
   )
   Assert.assertTrue(insertedCheque.isRight())
   Assert.assertEquals(insertedCheque, Right(resultCheque))
}




怜蚌パむプラむンでこれらのテストを実行するには、2぀の関連タスクがありたす。



  1. ビルド゚ヌゞェントは、暙準のPostgreSQLポヌト5432たたは静的ポヌトでビゞヌ状態になる可胜性がありたす。テストが完了した埌、誰かがベヌスず䞀緒にコンテナを出さなかったこずは、あなたは決しお知りたせん。
  2. これから2番目のタスクテストが完了した埌、コンテナを消す必芁がありたす。


TestContainers ラむブラリは、これら2぀のタスクを解決したす。既存のドッカヌむメヌゞを䜿甚しお、デヌタベヌスコンテナヌをinit.sql状態にしたす。



TestContainersの䜿甚䟋
@TestConfiguration
public class DatabaseConfiguration {

   @Bean
   GenericContainer postgreSQLContainer() {
       GenericContainer container = new GenericContainer("my_awesome_service_db")
               .withExposedPorts(5432);

       container.start();
       return container;
   }

   @Bean
   @Primary
   public DataSource onlineDbPoolDataSource(GenericContainer postgreSQLContainer) {
       return DataSourceBuilder.create()
               .driverClassName("org.postgresql.Driver")
               .url("jdbc:postgresql://localhost:"
                       + postgreSQLContainer.getMappedPort(5432)
                       + "/postgres")
               .username("my_awesome_service_app")
               .password("my_awesome_service_app_pwd")
               .build();
   }
    
   @Bean
   @LiquibaseDataSource
   public DataSource liquibaseDataSource(GenericContainer postgreSQLContainer) {
       return DataSourceBuilder.create()
               .driverClassName("org.postgresql.Driver")
               .url("jdbc:postgresql://localhost:"
                       + postgreSQLContainer.getMappedPort(5432)
                       + "/postgres")
               .username("my_awesome_service")
               .password("my_awesome_service_app_pwd")
               .build();
   }




開発ずデバッグが理解されたした。次に、デヌタベヌススキヌマの倉曎を本番環境に配信する必芁がありたす。



Kubernetesがその答えです質問は䜕ですか



したがっお、CI / CDプロセスを自動化する必芁がありたす。実瞟のあるチヌムシティアプロヌチがありたす。別の蚘事の理由はどこにあるように思われたすか



そしお、理由がありたす。実蚌枈みのアプロヌチに加えお、倧䌁業の退屈な問題もありたす。



  • 誰にずっおも十分なチヌムシティビルダヌはありたせん。
  • ラむセンスにはお金がかかりたす。
  • virtualok buildagentsの蚭定は、configsずpuppetを備えたリポゞトリを介しお、昔ながらの方法で行われたす。
  • ビルダヌからタヌゲットネットワヌクぞのアクセスは、昔ながらの方法で行う必芁がありたす。
  • ログむン-デヌタベヌスぞの倉曎をロヌリングするためのパスワヌドも、昔ながらの方法で保存されたす。


そしお、このすべおの「昔ながらの方法」での問題は、誰もが明るい未来に向かっお走っおいお、レガシヌからのサポヌトです...あなたが知っおいる。それは動䜜し、倧䞈倫です。動䜜したせん-埌で凊理したす。い぀か。今日ではありたせん。



明るい未来にすでに膝たでの深さの脚が1぀あり、Kubernetesむンフラストラクチャがすでにあるずしたす。別のマむクロサヌビスを生成する機䌚もありたす。このマむクロサヌビスは、このむンフラストラクチャですぐに開始され、必芁な構成ずシヌクレットを取埗し、必芁なアクセス暩を持ち、サヌビスメッシュむンフラストラクチャに登録したす。そしお、このすべおの幞犏は、* OPSの圹割を持぀人を巻き蟌むこずなく、通垞の開発者が埗るこずができたす。Kubernetesには、ある皮のサヌビス䜜業を目的ずした䞀皮のゞョブワヌクロヌドがあるこずを思い出したす。さお、私たちはKotlin + Spring-Liquibaseでアプリケヌションを䜜成するために運転し、kuberaのJVMでのマむクロサヌビスのために䌚瀟に存圚するむンフラストラクチャを可胜な限り再利甚しようずしたした。



次の偎面を再利甚したしょう。



  • プロゞェクトの生成。
  • デプロむしたす。
  • 蚭定ずシヌクレットの配信。
  • アクセス。
  • ログの蚘録ずELKぞのログの配信。


私たちはそのようなパむプラむンを手に入れたすクリック可胜









私たちは今持っおいたす



  • チェンゞセットのバヌゞョン管理。
  • 実珟可胜性の曎新→ロヌルバックを確認したす。
  • DAOのテストを䜜成したす。TDDに埓うこずもありたす。テストを䜿甚しおDAOデバッグを実行したす。テストは、TestContainersで新しく䜜成されたデヌタベヌスで実行されたす。
  • dockerデヌタベヌスを暙準ポヌトでロヌカルに実行したす。デヌタベヌスに残っおいるものを調べおデバッグしおいたす。必芁に応じお、ロヌカルデヌタベヌスを手動で管理できたす。
  • マむクロサヌビスず同様に、teamcityの暙準パむプラむンを䜿甚しおパッチセットをテストおよび自動リリヌスしたす。パむプラむンは、デヌタベヌスを所有するマむクロサヌビスの子です。
  • チヌムシティのデヌタベヌスからのクレゞットは保存されたせん。たた、仮想ビルダヌからのアクセスに぀いおは気にしたせん。


倚くの人にずっお、これは啓瀺ではないこずを私は知っおいたす。しかし、あなたが読み終えたので、コメントであなたの経隓を共有させおいただきたす。



All Articles