はじめに (対象読者・この記事でわかること)

この記事は、サーバー管理やCI/CDパイプラインの構築に携わっている開発者やDevOpsエンジニアを対象にしています。特に、システムの更新作業中に発生するダウンタイムを最小限に抑えたいと考えている方々に向けています。

この記事を読むことで、再起動が必要なツールを再起動せずに更新するための具体的な手法とその名称について理解できます。また、ローリング更新やブルーグリーンデプロイといった実践的な手法を学び、自社のシステムに適用するための知識を得ることができます。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。 - Linuxサーバーの基本的な操作知識 - CI/CDパイプラインの基本的な概念 - コンテナ技術(Dockerなど)の基本的な理解 - ロードバランサーやプロキシサーバーの基本的な仕組み

再起動不要な更新手法の概要と背景

多くのソフトウェアやツールは更新時に再起動が必要です。特にシステム全体の更新を行う場合、サービスを停止して再起動する必要があるため、ユーザーへの影響(ダウンタイム)が発生します。この問題を解決するための手法として、再起動を不要とする更新技術が開発されてきました。

これらの手法にはそれぞれ固有の名称があり、代表的なものとして「ローリング更新」「ブルーグリーンデプロイ」「カナリアリリース」などがあります。これらの手法は、既存のシステムを停止せずに新しいバージョンを段階的に投入することで、サービスの継続性を確保します。

再起動不要な更新手法の具体的な実装方法

ここでは、代表的な再起動不要な更新手法とその具体的な実装方法について解説します。

ローリング更新(Rolling Update)

ローリング更新は、システムを複数のインスタンスに分散させ、1つずつ更新していく手法です。すべてのインスタンスが更新されるまで、サービスは継続的に提供されます。

ステップ1:システムの分散化

まず、システムを複数のインスタンスに分散させます。例えば、Webアプリケーションの場合、複数のサーバーインスタンスをロードバランサーの背後に配置します。

Bash
# 例:AWS EC2インスタンスの作成 aws ec2 run-instances --image-id ami-0abcdef1234567890 --count 3 --instance-type t2.micro

ステップ2:更新プロセスの設定

次に、更新プロセスを自動化するためのスクリプトやCI/CDパイプラインを設定します。ここでは、TerraformとAWS CodePipelineを使用した例を示します。

Hcl
# 例:Terraformによるロードバランサーとターゲットグループの設定 resource "aws_lb_target_group" "app" { name = "app-target-group" port = 80 protocol = "HTTP" vpc_id = aws_vpc.main.id } resource "aws_lb" "app" { name = "app-load-balancer" internal = false load_balancer_type = "application" security_groups = [aws_security_group.lb.id] subnets = aws_subnet[*].id }

ステップ3:更新の実行

更新を実行する際は、ロードバランサーから1つのインスタンスを一時的に切り離し、更新を行います。更新が完了したら、インスタンスを再度ロードバランサーに戻します。

Bash
# 例:ターゲットグループからのインスタンスの登録解除 aws elbv2 deregister-targets --target-group-arn arn:aws:elasticloadbalancing:region:account-id:targetgroup/app-target-group/1234567890123456 --targets Id=i-1234567890abcdef0 # インスタンスの更新(例:アプリケーションのデプロイ) ssh -i key.pem user@instance_ip "sudo systemctl restart myapp" # 更新後、ターゲットグループへの再登録 aws elbv2 register-targets --target-group-arn arn:aws:elasticloadbalancing:region:account-id:targetgroup/app-target-group/1234567890123456 --targets Id=i-1234567890abcdef0

ブルーグリーンデプロイ(Blue-Green Deployment)

ブルーグリーンデプロイは、現在稼働している環境(ブルー)と新しい環境(グリーン)を並行して用意し、切り替える手法です。

ステップ1:環境の準備

まず、現在稼働している環境(ブルー)と同等の新しい環境(グリーン)を準備します。例えば、Docker Composeを使用した場合の設定は以下のようになります。

Yaml
# docker-compose.yml version: '3' services: app-blue: image: myapp:blue ports: - "80:80" environment: - ENVIRONMENT=blue networks: - app-network app-green: image: myapp:green ports: - "81:80" environment: - ENVIRONMENT=green networks: - app-network deploy: replicas: 1 networks: app-network: driver: bridge

ステップ2:切り替えの準備

新しい環境(グリーン)でアプリケーションを起動し、正常に動作することを確認します。

Bash
# グリーン環境の起動 docker-compose up -d app-green # グリーン環境の正常性確認 curl http://localhost:81/health

ステップ3:切り替えの実行

正常性が確認できたら、ロードバランサーをグリーン環境に向けて切り替えます。

Bash
# Nginxの設定変更(例) echo "upstream app { server app-green:80; }" > /etc/nginx/conf.d/app.conf # Nginxの再起動 sudo systemctl reload nginx

カナリアリリース(Canary Release)

カナリアリリースは、新しいバージョンを一部のユーザーにのみ提供し、問題がないか確認する手法です。

ステップ1:トラフィック分割の設定

まず、トラフィックを分割する設定を行います。例えば、Istioを使用した場合の設定は以下のようになります。

Yaml
# IstioのDestinationRuleとVirtualServiceの例 apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: myapp spec: host: myapp subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: myapp spec: hosts: - myapp http: - route: - subset: v1 weight: 90 - subset: v2 weight: 10

ステップ2:新しいバージョンのデプロイ

新しいバージョンをデプロイし、トラフィックの一部を新しいバージョンに向けます。

Bash
# 新しいバージョンのデプロイ kubectl apply -f myapp-v2.yaml # トラフィックの割合を変更(例:10%から50%に) kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: myapp spec: hosts: - myapp http: - route: - subset: v1 weight: 50 - subset: v2 weight: 50 EOF

ステップ3:監視と問題発生時の対応

新しいバージョンが正常に動作しているか監視し、問題が発生した場合は迅速にロールバックします。

Bash
# 新しいバージョンの監視 kubectl logs -l app=myapp,version=v2 # 問題発生時のロールバック kubectl apply -f myapp-v1.yaml

ハマった点やエラー解決

ローリング更新やブルーグリーンデプロイを実装する際に、以下のような問題に遭遇することがあります。

問題1:セッションデータの保持

ステートフルなアプリケーションの場合、インスタンスを切り替える際にセッションデータが失われる可能性があります。

解決策

セッションデータを外部ストレージ(Redisやデータベース)に保存するようにアプリケーションを修正します。例えば、Express.jsを使用している場合、以下のようにセッションストアを設定します。

Javascript
const express = require('express'); const session = require('express-session'); const RedisStore = require('connect-redis')(session); const redis = require('redis'); const app = express(); const redisClient = redis.createClient({ host: 'redis-server', port: 6379 }); app.use(session({ store: new RedisStore({ client: redisClient }), secret: 'your-secret-key', resave: false, saveUninitialized: false }));

問題2:データベーススキーマの更新

データベースのスキーマを更新する場合、古いバージョンのアプリケーションでは新しいスキーマに対応できない可能性があります。

解決策

データベースマイグレーションツール(FlywayやLiquibase)を使用して、スキーマの更新を管理します。例えば、Flywayを使用した場合のマイグレーションファイルは以下のようになります。

Sql
-- V1__Initial_schema.sql CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) NOT NULL UNIQUE ); -- V2__Add_password_column.sql ALTER TABLE users ADD COLUMN password VARCHAR(100) NOT NULL;

まとめ

本記事では、再起動が必要なツールを再起動せずに更新するための手法とその名称について解説しました。

  • ローリング更新:システムを複数のインスタンスに分散させ、1つずつ更新していく手法
  • ブルーグリーンデプロイ:現在稼働している環境と新しい環境を並行して用意し、切り替える手法
  • カナリアリリース:新しいバージョンを一部のユーザーにのみ提供し、問題がないか確認する手法

この記事を通して、システムのダウンタイムを最小限に抑えるための具体的な手法を学び、自社のシステムに適用するための知識を得ることができたと思います。

今後は、これらの手法を組み合わせたハイブリッドなデプロイ戦略や、クラウドネイティブ環境でのデプロイ自動化についても記事にする予定です。

参考資料