Vagrantを使ったPostgreSQL9.6非同期レプリケーションの環境構築
はじめに
とあるリリース前のサービスの負荷試験をやったときに、将来的にDBを複数台使用するとして、Webアプリケーションをどのように変更したら良いか分からなかった。
WebアプリケーションはSpringBootを使用しており、アプリケーションの機能追加で、DBの負荷分散に対応するようにした。
その時の記事はこれ↓
そんな経緯があったので、アプリケーションを改造する前に、AWSでリードレプリカを使用するのを想定して、PostgreSQLで非同期レプリケーション環境が開発環境に欲しかった。
なので、今回はPostgreSQLで非同期レプリケーション環境をVagrantで構築したお話。
目次
参考サイト
以下のサイトを参考にした。
環境
- CentOS 7
- PostgreSQL 9.6
- マスタ側IP:192.168.35.10
- スレーブ側IP:192.168.35.11
マスタ側の構築スクリプトの作成
マスタ側のプロビジョニングスクリプトを作成する。
マスタ側のプロビジョニングスクリプトは以下の感じ
ファイル名は「create_db_master_server.sh」で保存
# いつものやーつ sudo yum update -y sudo yum install vim wget -y # 時刻を日本にする sudo cp -p /usr/share/zoneinfo/Japan /etc/localtime # 時刻を自動で調整するようにする cp /etc/chrony.conf /etc/chrony.conf_preprovision sudo sed -i -e "s/server 0.centos.pool.ntp.org iburst/server ntp.nict.jp/g" /etc/chrony.conf sudo sed -i -e "s/server 1.centos.pool.ntp.org iburst/server ntp1.jst.mfeed.ad.jp/g" /etc/chrony.conf sudo sed -i -e "s/server 2.centos.pool.ntp.org iburst/server ntp2.jst.mfeed.ad.jp/g" /etc/chrony.conf sudo sed -i -e "s/server 3.centos.pool.ntp.org iburst/server ntp3.jst.mfeed.ad.jp/g" /etc/chrony.conf sudo chronyc -a makestep # PostgreSQLのインストール sudo rpm -U https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm sudo yum install -y postgresql96 postgresql96-server # postgresユーザで「initdb」を実行するため、rootユーザになる sudo su - sudo su postgres -c "/usr/pgsql-9.6/bin/initdb /var/lib/pgsql/9.6/data/" PG_DIR="/var/lib/pgsql/9.6/data/" sudo su postgres -c "cp ${PG_DIR}postgresql.conf ${PG_DIR}postgresql.conf_preprovision" # 接続受付IPを変更 sudo sed -i -e "s/#listen_addresses = 'localhost'/listen_addresses = '*'/g" ${PG_DIR}postgresql.conf # 各ログの設定を変更 # Webアプリの接続先が読み取り用と更新用で分かれるか確認のため、 # 「log_connections」、「log_disconnections 」を有効にする sudo sed -i -e "s/log_filename = 'postgresql-%a.log'/log_filename = 'postgresql-%m%d.log'/g" ${PG_DIR}postgresql.conf sudo sed -i -e "s/#log_checkpoints = off/log_checkpoints = on/g" ${PG_DIR}postgresql.conf sudo sed -i -e "s/#log_connections = off/log_connections = on/g" ${PG_DIR}postgresql.conf sudo sed -i -e "s/#log_disconnections = off/log_disconnections = on/g" ${PG_DIR}postgresql.conf sudo sed -i -e "s/#log_lock_waits = off/log_lock_waits = on/g" ${PG_DIR}postgresql.conf sudo sed -i -e "s/log_timezone = 'UTC'/log_timezone = 'Japan'/g" ${PG_DIR}postgresql.conf # レプリケーション設定の変更 # https://qiita.com/U_ikki/items/e117acad0413546d6923 sudo sed -i -e "s/#checkpoint_timeout = 5min/checkpoint_timeout = 15min/g" ${PG_DIR}postgresql.conf sudo sed -i -e "s/#hot_standby = off/hot_standby = on/g" ${PG_DIR}postgresql.conf sudo sed -i -e "s/#wal_level = minimal/wal_level = replica/g" ${PG_DIR}postgresql.conf sudo sed -i -e "s/#max_wal_senders = 0/max_wal_senders = 3/g" ${PG_DIR}postgresql.conf # 接続許可の設定を変更 sudo su postgres -c "cp ${PG_DIR}pg_hba.conf ${PG_DIR}pg_hba.conf_preprovision" sudo sed -i -e "87i host replication postgres 192.168.35.0/24 trust" ${PG_DIR}pg_hba.conf sudo sed -i -e "88i host all all 192.168.35.0/24 md5" ${PG_DIR}pg_hba.conf # 参照先DNSサーバの変更(今回あまり関係なし) sudo cp /etc/NetworkManager/NetworkManager.conf /etc/NetworkManager/NetworkManager.conf_preprovision sudo sed -i -e "26i dns=none" /etc/NetworkManager/NetworkManager.conf sudo systemctl restart NetworkManager sudo sed -i -r "s/nameserver [\.0-9]+/nameserver 192.168.35.20/g" /etc/resolv.conf # 自動起動ON&PostgreSQL起動 sudo systemctl enable postgresql-9.6 sudo systemctl start postgresql-9.6 # アカウントのパスワードを変更するSQLを実行 sudo psql -U postgres sample_db < /vagrant/db/set_password_to_db.sql
スレーブ側の構築スクリプトの作成
スレーブ側のプロビジョニングスクリプトを作成する。
スレーブ側のプロビジョニングスクリプトは以下の感じ
ファイル名は「create_db_slave_server.sh」で保存する。
# いつものやーつ sudo yum update -y sudo yum install vim wget -y # 時刻を日本にする sudo cp -p /usr/share/zoneinfo/Japan /etc/localtime # 時刻を自動で調整するようにする cp /etc/chrony.conf /etc/chrony.conf_preprovision sudo sed -i -e "s/server 0.centos.pool.ntp.org iburst/server ntp.nict.jp/g" /etc/chrony.conf sudo sed -i -e "s/server 1.centos.pool.ntp.org iburst/server ntp1.jst.mfeed.ad.jp/g" /etc/chrony.conf sudo sed -i -e "s/server 2.centos.pool.ntp.org iburst/server ntp2.jst.mfeed.ad.jp/g" /etc/chrony.conf sudo sed -i -e "s/server 3.centos.pool.ntp.org iburst/server ntp3.jst.mfeed.ad.jp/g" /etc/chrony.conf sudo chronyc -a makestep # PostgreSQLのインストール sudo rpm -U https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm sudo yum install -y postgresql96 postgresql96-server # postgresユーザで「pg_basebackup」を実行するため、rootユーザになる sudo su - sudo su postgres -c "pg_basebackup -h 192.168.35.10 -D /var/lib/pgsql/9.6/data/ -X stream --progress -U postgres -R" # 参照先DNSサーバの変更(今回あまり関係なし) sudo cp /etc/NetworkManager/NetworkManager.conf /etc/NetworkManager/NetworkManager.conf_preprovision sudo sed -i -e "26i dns=none" /etc/NetworkManager/NetworkManager.conf sudo systemctl restart NetworkManager sudo sed -i -r "s/nameserver [\.0-9]+/nameserver 192.168.35.20/g" /etc/resolv.conf # 自動起動ON&PostgreSQL起動 sudo systemctl enable postgresql-9.6 sudo systemctl start postgresql-9.6
仮想マシンの設定ファイルの作成
仮想マシンのCPU数・メモリサイズを定義する設定ファイルを用意する。
設定ファイル名は「vmconf.yml」で保存
# 仮想マシンのスペック設定 vm: # DBサーバ(マスター・スレーブ共通) db: cpus: 2 memory: 1024
マスター・スレーブ両方同じ設定を使用
今回はCPUコア数2個、メモリサイズ1GBに設定
Vagrantファイルの作成
# -*- mode: ruby -*- # vi: set ft=ruby : require 'yaml' settings = YAML.load_file 'vmconf.yml' Vagrant.configure("2") do |config| config.vm.box = "centos/7" # postgresql マスターサーバ config.vm.define :db_master do |define| define.vm.hostname = "db-master" define.vm.provision "db-master", type: "shell", :path => "create_db_master_server.sh", :privileged => false define.vm.network "forwarded_port", guest: 5432, host: 5432 define.vm.network "private_network", ip: "192.168.35.10" define.vm.provider "virtualbox" do |provider| provider.name = "db-master" provider.cpus = settings['vm']['db']['cpus'] provider.memory = settings['vm']['db']['memory'] end end # postgresql スレーブサーバ config.vm.define :db_slave do |define| define.vm.hostname = "db-slave" define.vm.provision "db-slave", type: "shell", :path => "create_db_slave_server.sh", :privileged => false define.vm.network "forwarded_port", guest: 5432, host: 55432 define.vm.network "private_network", ip: "192.168.35.11" define.vm.provider "virtualbox" do |provider| provider.name = "db-slave" provider.cpus = settings['vm']['db']['cpus'] provider.memory = settings['vm']['db']['memory'] end end end
おわりに
レプリケーション構成のPostgreSQLを開発環境に構築することができ、この環境をもとにアプリケーションの改造が出来た。
SpringBootで接続先のデータベースを動的に切り替える
はじめに
とあるサービスを作成することになり、想定する利用人数をもとに、負荷分散について調べていた。
現状大丈夫そうだなと思っていても、今後利用人数が増えることを考えると、DBの負荷分散はどうしたら良いのか分からなかった。
負荷分散について考える前までは、レプリケーションやら、AWSのリードレプリカの概要・説明が出てくるけど、アプリケーションの変更が必要なのか良く分からなかった。
調べてみると、やっぱりアプリケーションの改造をやるか、PostgreSQLの場合はpgpoolのようなミドルウェアが必要っぽい。
AWS上の環境をあまり増やしたくないので、SpringBootで作成したアプリケーションに、DBの接続先を切り替える機能を作ることにした。
目次
- はじめに
- 参考サイト
- 環境
- 作業の概要
- 1. コンテキストを保持するクラスを作成
- 2. コンテキストより、使用するBean名を返すクラスの作成
- 3. 接続先を指定するアノテーションを作成
- 4. コンテキストを切り替えるAspectを作成
- 5. 読み取り用と更新用とそれぞれのDB接続設定を追加
- 6. Beanを作成
- 7. コントローラにアノテーションを付与
- おわりに
参考サイト
以下のサイトを参考にした。
環境
- Java 1.8
- Spring Boot 1.5.4.RELEASE
- PostgreSQL 9.6
AWS上で想定している将来の環境は以下の感じ。
ということで、開発環境にDNSサーバ・レプリケーションを構成済みのPostgreSQL2台を構築した。
DBのロードバランスは、DNSサーバにお任せすることにした。
作業の概要
いろいろやることがあったので、ざっくり以下にまとめ
- コンテキストを保持するクラスを作成
- コンテキストより、使用するBean名を返すクラスの作成
- 接続先を指定するアノテーションを作成
- コンテキストを切り替えるAspectを作成
- 読み取り用と更新用とそれぞれのDB接続設定を追加
- Beanを作成
- コントローラにアノテーションを付与
サービスにアノテーションを付与するようにしたかったけれども、サービスに付与するとテストが通らなった。
コントローラに付与すると問題なく動作し、また既に色々処理が出来てしまっていたので、今回はコントローラにアノテーションを付与ようにした。
(コントローラのメソッドの中で、「読み込み用」のサービスメソッドの次に「更新用」サービスメソッドを呼んでいるのが原因。"このコネクションじゃDBの更新は出来ないよ"みたいなことを言われていた気がする。ビジネスロジックが1つのサービスのメソッドでまとまっていれば、サービスにアノテーション付与出来たと思う)
1. コンテキストを保持するクラスを作成
まずは、Enumを作っておく。
public enum DataSourceType { ReadOnly, Updatable, }
接続先の設定を保持するコンテキストクラスを以下のように作成
public class DbContextHolder { private static ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>(); public static void setDataSourceType(DataSourceType type) { contextHolder.set(type); } public static DataSourceType getDataSourceType() { return contextHolder.get(); } public static void clear() { contextHolder.remove(); } }
2. コンテキストより、使用するBean名を返すクラスの作成
AbstractRoutingDataSourceを継承したクラスを作成。(定数文字列:READ_ONLY_DATA_SOURCE_NAME、UPDATABLE_DATA_SOURCE_NAMEは別のクラスで定義している)
public class RoutingDataSourceResolver extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { if (DbContextHolder.getDataSourceType() == null) { return null; } switch (DbContextHolder.getDataSourceType()) { case ReadOnly: return READ_ONLY_DATA_SOURCE_NAME; case Updatable: return UPDATABLE_DATA_SOURCE_NAME; default: throw new RuntimeException("unknown datasource"); } } }
3. 接続先を指定するアノテーションを作成
Controllerクラスのメソッドに付与し、「読み込み用」か「更新用」どちらに接続するかを設定するアノテーションを作成
@Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { DataSourceType value(); }
4. コンテキストを切り替えるAspectを作成
先程のアノテーションをもとに、コンテキストを設定するアスペクトを作成する。
今回はコントローラのメソッド単位で、接続先を切り替えるので、「@Controller」、「@RestController」が付いていないクラスから呼ばれると例外を投げるようにした。
@Aspect @Component public class SwitchingDataSourceAspect { @Around("@annotation(com.example.sample.aspect.DataSource)") public Object proceed(ProceedingJoinPoint joinPoint) throws Throwable { try { DataSourceType type = this.getDataSourceType(joinPoint); DbContextHolder.setDataSourceType(type); return joinPoint.proceed(); } finally { DbContextHolder.clear(); } } private DataSourceType getDataSourceType(JoinPoint joinPoint) throws NoSuchMethodException { MethodSignature signature = (MethodSignature)joinPoint.getSignature(); String methodName = signature.getMethod().getName(); Class<?>[] parameterTypes = signature.getMethod().getParameterTypes(); Method method = joinPoint.getTarget().getClass().getMethod(methodName, parameterTypes); Controller controller = joinPoint.getTarget().getClass().getAnnotation(Controller.class); RestController restController = joinPoint.getTarget().getClass().getAnnotation(RestController.class); DataSource dataSource = method.getAnnotation(DataSource.class); if (controller == null && restController == null) { throw new IllegalArgumentException("@Datasource is only can use in @Controller or @RestController."); } return dataSource.value(); } }
5. 読み取り用と更新用とそれぞれのDB接続設定を追加
SpringBootの設定ファイルに「readOnly」と「updatable」を追加。
spring: datasource: readOnly: driverClassName: org.postgresql.Driver url: jdbc:postgresql://read-only-db.sample.internal:5432/sample_db username: xxxxxxxx password: xxxxxxxx minIdlePoolSize: 3 maximumPoolSize: 40 idleTimeout_ms: 300000 maxLifetime_ms: 1800000 updatable: driverClassName: org.postgresql.Driver url: jdbc:postgresql://updatable.sample.internal:5432/sample_db username: xxxxxxxx password: xxxxxxxx minIdlePoolSize: 3 maximumPoolSize: 20 idleTimeout_ms: 300000 maxLifetime_ms: 1800000
6. Beanを作成
設定ファイルをもとに、DataSourceを生成するBeanを作成。
DBマイグレーションでFlywayを使用している場合、更新用のBeanに「@FlywayDataSource」を付与する。これがないと起動できなかった。
RoutingDataSourceResolverを返すBean(multiDataSource)に「@Primary」を付与する。これがないと、「multiDataSource、readOnlyDataSource、updatableDataSourceどれを使うんだよ?」的なことを言われて起動できなかった。
@Configuration public class WebConfig extends WebMvcConfigurerAdapter { public static final String READ_ONLY_DATA_SOURCE_NAME = "readOnlyDataSource"; public static final String UPDATABLE_DATA_SOURCE_NAME = "updatableDataSource"; @Autowired private Environment environment; @Autowired @Qualifier(READ_ONLY_DATA_SOURCE_NAME) private DataSource readableDataSource; @Autowired @Qualifier(UPDATABLE_DATA_SOURCE_NAME) private DataSource updatableDataSource; @Bean @Primary public RoutingDataSourceResolver multiDataSource() { RoutingDataSourceResolver resolver = new RoutingDataSourceResolver(); // スイッチするデータソースを設定 Map<Object, Object> dataSources = new HashMap<>(); dataSources.put(READ_ONLY_DATA_SOURCE_NAME, readableDataSource); dataSources.put(UPDATABLE_DATA_SOURCE_NAME, updatableDataSource); resolver.setTargetDataSources(dataSources); resolver.setDefaultTargetDataSource(updatableDataSource); return resolver; } @Bean(READ_ONLY_DATA_SOURCE_NAME) public DataSource readOnlyDataSource() { String baseConfig = "spring.datasource.readOnly.%s"; HikariConfig config = new HikariConfig(); config.setDriverClassName(environment.getProperty(String.format(baseConfig, "driverClassName"))); config.setJdbcUrl(environment.getProperty(String.format(baseConfig, "url"))); config.setUsername(environment.getProperty(String.format(baseConfig, "username"))); config.setPassword(environment.getProperty(String.format(baseConfig, "password"))); HikariDataSource dataSource = new HikariDataSource(config); dataSource.setMinimumIdle(Integer.parseInt(environment.getProperty(String.format(baseConfig, "minIdlePoolSize")))); dataSource.setMaximumPoolSize(Integer.parseInt(environment.getProperty(String.format(baseConfig, "maximumPoolSize")))); dataSource.setIdleTimeout(Long.parseLong(environment.getProperty(String.format(baseConfig, "idleTimeout_ms")))); dataSource.setMaxLifetime(Long.parseLong(environment.getProperty(String.format(baseConfig, "maxLifetime_ms")))); return dataSource; } @Bean(UPDATABLE_DATA_SOURCE_NAME) @FlywayDataSource public DataSource updatableDataSource() { String baseConfig = "spring.datasource.updatable.%s"; HikariConfig config = new HikariConfig(); config.setDriverClassName(environment.getProperty(String.format(baseConfig, "driverClassName"))); config.setJdbcUrl(environment.getProperty(String.format(baseConfig, "url"))); config.setUsername(environment.getProperty(String.format(baseConfig, "username"))); config.setPassword(environment.getProperty(String.format(baseConfig, "password"))); HikariDataSource dataSource = new HikariDataSource(config); dataSource.setMinimumIdle(Integer.parseInt(environment.getProperty(String.format(baseConfig, "minIdlePoolSize")))); dataSource.setMaximumPoolSize(Integer.parseInt(environment.getProperty(String.format(baseConfig, "maximumPoolSize")))); dataSource.setIdleTimeout(Long.parseLong(environment.getProperty(String.format(baseConfig, "idleTimeout_ms")))); dataSource.setMaxLifetime(Long.parseLong(environment.getProperty(String.format(baseConfig, "maxLifetime_ms")))); return dataSource; } }
setDefaultTargetDataSourceメソッドに「updatableDataSource」を突っ込んでいるので、@DataSourceのつけ忘れがあった場合、更新用のDBに接続してくれるはず(ちゃんと調べてない)
DataSourceにはHikariDataSourceを使用するようにした。
良くBasicDataSourceを使っているサンプルを見かけるけど、Webサーバを起動したままPostgreSQLを再起動すると、初回アクセスのときにDBコネクションエラーが発生する。
HikariDataSourceだと勝手にコネクションを再生成してくれて、初回アクセスでも問題なかった。
BasicDataSourceでも、testOnBorrowとかvalidationQueryを設定すれば、再接続してくれるっぽいけど・・・残念ながらうまくいったことがない・・・
あと、BasicDataSourceを使用して実際に負荷をかけてみたところ、上限無しにコネクションを生成し、「クライアント多すぎぃ」とDB怒られた・・・確かにコネクションの上限設定してなかったもんね。
上限の設定について一応調べた気がするけど、面倒くさくなった
ということがあったので、DataSourceにはHikariDataSourceを使用するようにした。(2回目)
7. コントローラにアノテーションを付与
接続先を切り替える処理が完成したので、あとはコントローラのメソッドに、@DataSourceを付与していく。
以下、雑なサンプルコード
@Controller @RequestMapping("api/v1/foo") public class FooController { private final FooService service; @Autowired public FooController(FooService service) { this.service= service; } // 読み取りのみの処理 @DataSource(DataSourceType.ReadOnly) @RequestMapping(value = "", method = RequestMethod.GET) public ResponseEntity getFoo() { return ResponseEntity.ok(this.service.getFoo()); } // 更新を含む処理 @DataSource(DataSourceType.Updatable) @RequestMapping(value = "", method = RequestMethod.DELETE) public ResponseEntity deleteFoo() { this.service.deleteFoo() return ResponseEntity.ok(new MessageOnly("Foo Deleted")); } }
おわりに
少々追加するクラスが多いものの、アノテーションを付与して、DBの接続先を切り替える機能を作成することができた。
SpringBootが設定を勝手にやってくれていたところが結構あり、色んなハマったポイントがあった。
利用人数が増加するアプリを作成するときは、一番最初にこの処理を作っておくべきだと思った。
そういえばDNSサーバの構築について書いてない・・・
今回、初めてCentOSでDNSサーバを構築したので、それについてはまたいつか・・・
追記。DNSサーバの構築についてはコチラ
Vagrantでロードバランサ+WEBサーバ×2+DBを構築
はじめに
Webアプリやら常駐サービスを開発するときに、いつも新しく仮想サーバを作成するのだけれども、容量がでかいし、構築に時間がかかる。
「いつか使うかも」って思って、全然消さなかったりする
一緒に開発するからといって、仮想サーバを渡しても、IPアドレスが変わったりして、普段気にしない環境が変わるので面倒臭い
あと開発に関わる人全員が、環境構築ができる訳ではないし
いい加減、環境構築のハードルを下げないとな~と思い、前々から使ってみようと思っていたVagrantを使ってみた
目次
構築する環境
- DBサーバ(PostgreSQL 9.6)
- CPU:2コア
- MEM:1GB
- nginx(ロードバランサ目的)
- CPU:1コア
- MEM:512MB
- Webサーバ(Tomcat 8.5)×2台
- CPU:2コア
- MEM:1GB
- 全部CentOS7
Vagrantにプラグインの追加
設定ファイルの転送等で共有フォルダを使用するが、共有フォルダをマウントする際にエラーが発生する。
エラーを防ぐため、以下のコマンドでプラグインを最初にインストールする。
参考サイト
vagrant plugin install vagrant-vbguest
ベースのbox作成
DBサーバ、WEBサーバを構築する前にCentOS7に共通な設定・更新を実施し、元になるboxを作成する。
※元になるboxを作成する前は、何回も仮想環境を作り直して、新しい仮想PCができるたびに「yum update」やら「Guest Additions」の更新が走ったりとかなり時間がかかった・・・
まず、作業ディレクトリにて「vagrant init」を実行
作成された「Vagrantfile」を以下のように編集した
# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.provider "virtualbox" do |vb| vb.memory = "512" end config.vm.provision "shell", inline: <<-SHELL # いつものやーつ sudo yum update -y sudo yum install vim wget -y # 時刻を日本にする sudo cp -p /usr/share/zoneinfo/Japan /etc/localtime # 時刻を自動で調整するようにする cp /etc/chrony.conf /etc/chrony.conf_preprovision sudo sed -i -e "s/server 0.centos.pool.ntp.org iburst/server ntp.nict.jp/g" /etc/chrony.conf sudo sed -i -e "s/server 1.centos.pool.ntp.org iburst/server ntp1.jst.mfeed.ad.jp/g" /etc/chrony.conf sudo sed -i -e "s/server 2.centos.pool.ntp.org iburst/server ntp2.jst.mfeed.ad.jp/g" /etc/chrony.conf sudo sed -i -e "s/server 3.centos.pool.ntp.org iburst/server ntp3.jst.mfeed.ad.jp/g" /etc/chrony.conf sudo chronyc -a makestep # 空き領域をゼロ埋めするために一時ディレクトリ/tmpに適当なファイルZEROを作成。中身はキッチリゼロ埋めされる。 sudo dd if=/dev/zero of=/tmp/ZERO bs=1M sudo rm -f /tmp/ZERO SHELL end
空き領域をゼロ埋めしているのは、boxサイズを小さくするため
以下のサイトのコメントを参考にした。
編集が終わったら以下のコマンドを実行し仮想OSを作成する
vagrant up
「vagrant up」完了後、パッケージ・boxを作成(名前はmy_base_centos7にした)
vagrant package vagrant box add my_base_centos7 package.box
もう要らないので後片付け
vagrant destroy
エクスプローラからpackage.boxの削除する
Vagrantfileの作成
Vagrantfileを以下のようにした
# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.box = "my_base_centos7" config.vm.synced_folder "./synced", "/vagrant_data", create: true config.vm.define :db do |db| db.vm.hostname = "mt-db" db.vm.provision "db", type: "shell", :path => "create_db_server.sh", :privileged => false db.vm.network :forwarded_port, id: "ssh", guest: 22, host: 7022 db.vm.network "forwarded_port", guest: 5432, host: 5432 db.vm.network "private_network", ip: "192.168.33.10" db.vm.provider "virtualbox" do |db_vb| db_vb.name = "mt_db" db_vb.cpus = 2 db_vb.memory = "1024" end end config.vm.define :proxy do |proxy| proxy.vm.hostname = "mt-proxy" proxy.vm.provision "proxy", type: "shell", :path => "create_proxy_server.sh", :privileged => false proxy.vm.network :forwarded_port, id: "ssh", guest: 22, host: 7122 proxy.vm.network "forwarded_port", guest: 8888, host: 8888 proxy.vm.network "private_network", ip: "192.168.33.11" proxy.vm.provider "virtualbox" do |proxy_vb| proxy_vb.name = "mt_proxy" proxy_vb.cpus = 1 proxy_vb.memory = "512" end end config.vm.define :web1 do |web1| web1.vm.hostname = "mt-web1" web1.vm.provision "web1", type: "shell", :path => "create_web_server.sh", :privileged => false web1.vm.network :forwarded_port, id: "ssh", guest: 22, host: 7222 web1.vm.network "private_network", ip: "192.168.33.12" web1.vm.provider "virtualbox" do |web1_vb| web1_vb.name = "mt_web1" web1_vb.cpus = 2 web1_vb.memory = "1024" end end config.vm.define :web2 do |web2| web2.vm.hostname = "mt-web2" web2.vm.provision "web2", type: "shell", :path => "create_web_server.sh", :privileged => false web2.vm.network :forwarded_port, id: "ssh", guest: 22, host: 7322 web2.vm.network "private_network", ip: "192.168.33.13" web2.vm.provider "virtualbox" do |web2_vb| web2_vb.name = "mt_web2" web2_vb.cpus = 2 web2_vb.memory = "1024" end end end
フォルダ構成はこんな感じ
C:. │ create_db_server.sh---------DB構築スクリプト │ create_proxy_server.sh------nginx構築スクリプト │ create_web_server.sh--------Tomcat構築スクリプト │ Vagrantfile │ ├─.vagrant │ └─synced--------仮想マシンと同期するフォルダ ├─db │ sample_db.dump.bz2---------DBダンプ │ set_password_to_db.sql-----パスワード設定SQL │ ├─proxy │ 404.html-------------NotFound画面 │ maintenance.html-----メンテナンス画面 │ nginx.conf-----------nginxの設定ファイル │ nginx.service--------自動起動の設定ファイル │ └─web sample.war----------配置WEBアプリ tomcat.service------自動起動の設定ファイル tomcat.sh-----------/etc/profile.dに配置
「create_db_server.sh」「create_proxy_server.sh」「create_web_server.sh」には、サーバを構築するためのコマンドが入っている。
あまり参考にならないであろうプロビジョニングスクリプト
create_db_server.sh(PostgreSQL構築)
参考
create_proxy_server.sh(ロードバランサnginx構築)
参考
create_web_server.sh(Webサーバ構築)
参考
終わりに
少々複雑なサーバ構成でも、他の人に渡すときはVagrantfile+αで済むのはありがたいし、変更内容は基本プロビジョニングした時のスクリプトを見れば大丈夫なのは良いですな。
ただプロビジョニングのスクリプトを作成するのには凄い時間が掛かった・・・
最初に共通で使用するboxを作成しておけば、もう少し苦労が少なかったかも