Kubernetes
Kubernetesとは
複数マシンから構成されるマシン基盤上でのコンテナ管理に用いられるプラットフォームです。 例えばアプリケーションAPIサーバーを起動するコンテナを常にn台に分散してデプロイさせたいとき、定義ファイルにそのようなデプロイ指示を記述すればKubernetesがよしなにデプロイを実施してくれます。
特徴
理想とするコンテナデプロイ状況を宣言的に記述できます。
先程の例のように、コンテナをどのようにデプロイしていくかをYAMLやJSON等のファイルに宣言し、それをもとにKubernetesを使用しでデプロイを実施できます。理想とするデプロイ状況がファイル上に定義されているので、定義ファイルがあればデプロイ状況が再現可能になります。またIaCの文脈で、コードとして管理することが可能です。これにより、Kubernetes上の定義をGitで管理したり、CLIの入力に使用するなどが可能になります。
Node
Kubernetesから見た(仮想・物理)マシンの単位です。マシンはオンプレミスにおける一台一台の物理サーバーや、クラウドプラットフォーム上のサーバーインスタンスに該当します。
Pod
Kubernetes上のデプロイ単位です。デプロイする単位なので、複数のコンテナをまとめて起動する場合も1Podとなります。 例えばdocker-composeで複数コンテナを連携してデプロイするような単位を1Podでまとめます。
Pod内ではネットワークインタフェース及びストレージが共有されます。そのため、Podを一つのコンテナホストとみなすこともできます。
Controller
Kubernetesではコンテナクラスタを制御するための仕組みをControllerとして提供します。
Deployment
Deploymentは基本的なResourceの一種で、Podを一定数維持しつつデプロイを実施するのに使用できます。 セルフヒーリング機能を持ち、もしNode障害などで一部のPodが機能しなくなっても、予め宣言しておいたPod数を維持するように生存が確認できるNodeに新たにPodがデプロイされます。
StatefulSet
本来コンテナはステートレス、すなわち状態を持たずコンテナが破棄された場合は内部のファイルシステムも破棄されます。 StatefulSetで管理されたPodは一意の識別子を割り当てられ、明確に区別されます。Node障害などで特定のPodがダウンしても、例えばそのPodに対応したVolumeは復帰後同じ識別子を持つPodに割り当てられます。このように、永続的な状態を持たせるためにStatefulSetを使用することができます。
参考
イラストでわかるDockerとKubernetes (Software Design plus) | 徳永 航平 |本 | 通販 | Amazon
更新履歴
2021/03/28 初版 2021/03/29 Pod,Deployment,StatefulSet 記述追加
【翻訳】Spring Security Architecture
これは何?
Javaプラットフォーム向けフレームワークSpring FrameworkのドキュメントSpring Security Architectureの翻訳記事です。
Spring Securityの提供する認証・認可等のセキュリティ機能をどのようなアーキテクチャで実現しているのかを概要レベルで説明しています。
意訳の部分も多いと思いますが、何卒ご了承ください。
誤訳・アドバイス等ありましたら、コメント頂けると助かります。
(特に誤訳が心配なので、都度修正していきます)
以下、訳文になります。
Spring Security アーキテクチャ
このガイドはSpring Securityの入門ドキュメントであり、Spring Securityフレームワークの基本的な設計・構成要素について説明します。
ごく基本的なアプリケーションセキュリティのみをカバーしていますが、Spring Securityを使う開発者が経験する諸々の混乱を解消する事ができるでしょう。
説明のため、Webアプリケーションに対してフィルターや一般的なメソッドアノテーションを使用し、セキュリティを担保するやり方を通じて、Spring Securityの概要を見ていきます。
あなたがアプリケーションをどのように高度にセキュア化するのか、どのようにカスタマイズできるのかを理解する必要があるとき、あるいはただ単にアプリケーションのセキュリティについて考えるとき、このドキュメントを活用してください。
このガイドはごく基本的な問題以上のものを解決するためのマニュアルやレシピではありません(そのために必要な資料が他にあります)が、ビギナーにはもちろん、上級者の方にも役に立つことでしょう。Spring Boot はセキュアなアプリケーションとして基本的な振る舞いを提供するので参考になりますし、Spring Securityが Spring Bootアーキテクチャ全体に対してどのように適合しているのかを理解するのにも役に立つでしょう。
アーキテクチャとアクセスコントロール
アプリケーションセキュリティは多かれ少なかれ、2つの独立した問題に集約されます - 認証(あなたは誰か?)と認可(あなたに許される行いは何か?)です。認可(authorization)のことをアクセスコントロール(access controll)と呼ぶ人もおり紛らわしいのですが、認可(authorization)という言葉が他の文脈では別の意味と捉えられる場合があるので、そのようなときには用語を使い分けると便利です。 Spring Securityでは認証を認可から分離するアーキテクチャを取り、認証・認可両者それぞれにおける戦略・拡張ポイントがあります。
認証
認証では AuthenticationManager
インタフェースを主に使用した方法を取ります。
AuthenticationManage
は下記の一つのメソッドを持ちます:
public interface AuthenticationManager { Authentication authenticate(Authentication authentication) throws AuthenticationException; }
AuthenticationManage
は authenticate()
メソッドの中で、以下の3つの処理を実行します:
入力が正しい認証情報である場合、
Authentication
インスタンスを返す(通常、authenticated=true
となる)。入力が誤った認証情報である場合、
AuthenticationException
をスローする。上記認証情報が判別不能の場合、
null
を返す。
AuthenticationException
は RuntimeException
です。多くの場合アプリケーションの目的やスタイルに依存した方法でアプリケーションによってハンドルされます。言い換えれば、通常であればユーザのコードはこの例外をキャッチしたりハンドルしたりすることを想定していないということです。例えば、WWW-Authenticate
ヘッダーの有無に関わらず、Web UI が認証失敗のページを表示し、バックエンドHTTPサーバが401レスポンスを返します。
AuthenticationManager
の最も一般的な実装は ProviderManager
です。 ProviderManager
は複数の AuthenticationProvider
インスタンスチェーンに処理を委譲します。 AuthenticationProvider
は AuthenticationManager
と少し似ていますが、AuthenticationProvider
は認証処理で使用できる Authentication
型を呼び出し元が問い合わせられるメソッドを持っています:
public interface AuthenticationProvider { Authentication authenticate(Authentication authentication) throws AuthenticationException; boolean supports(Class authentication); }
supports()
の Class<?>
引数は実際には Class<? extends Authentication>
型です( authenticate()
に渡される引数型としてサポートするかどうかだけを問い合わせます)。 ProviderManager
は複数の AuthenticationProvider
チェーンに認証処理を委譲することで、複数の異なる認証メカニズムをサポートすることができます。もし ProviderManager
が特定の Authentication
インスタンス型を認識していない場合、この処理はスキップされます。
ProviderManager
は任意で親クラスを持つことができます。この親クラスは小クラスの全ての AuthenticationProvider
が null
であるとき参照されます。 親クラスが使用できないとき、 null
Authentication
となり AuthenticationException
例外が送出されます。
アプリケーションが論理的に保護されたリソースを持つと気(例:全てのWebリソースがパスパターン /api/**
にマッチする)、各リソースグループはそれぞれ自身の AuthenticationManager
を持ちます。 殆どの場合、それらは ProviderManager
であり、それらは親を共有しています。このとき親は"グローバルな"リソースであり、全てのproviderのフォールバックとしての役割を持ちます。
[figure1]
Authentication Managers のカスタマイズ
Spring Securityは標準的なAuthentication Managerの特徴を取得し、アプリケーションにセットアップするための構成ヘルパーを提供します。最も一般的なユーザヘルパーは AuthenticationManagerBuilder
で、JDBCやLDAP user details、あるいはカスタム UserDetailsService
による追加 user details をインメモリでセットアップするのに向いています。
以下は、グローバルな(親の) AuthenticationManager
を構成するアプリケーションの例です。
@Configuration public class ApplicationSecurity extends WebSecurityConfigurerAdapter { ... // web stuff here @Autowired public void initialize(AuthenticationManagerBuilder builder, DataSource dataSource) { builder.jdbcAuthentication().dataSource(dataSource).withUser("dave") .password("secret").roles("USER"); } }
この例はWebアプリケーションに関連したものですが、 ここで示した AuthenticationManagerBuilder
の使い方はより広い領域に応用できます(以下のWebアプリケーションセキュリティの実装方法の詳細を見てください)。 ApplicationManagerBuilder
は @Bean
のメソッド内で @Autowired
されます - このようにしてグローバルな(親の) AuthenticationManager
が作成されます。
対象的に、以下のようにした場合:
@Configuration public class ApplicationSecurity extends WebSecurityConfigurerAdapter { @Autowired DataSource dataSource; ... // web stuff here @Override public void configure(AuthenticationManagerBuilder builder) { builder.jdbcAuthentication().dataSource(dataSource).withUser("dave") .password("secret").roles("USER"); } }
AuthenticationManagerBuilder
は "ローカルの" AuthenticationManager
を生成するために使われます。この AuthenticationManager
はグローバルな AuthenticationManager
の子インスタンスです。Spring Bootアプリケーションでは別のBean内でグローバルな AuthenticationManager
を@Autowired
できますが、ローカルな AuthenticationManager
は明示的な宣言なしに作成することはできません。
AuthenticationManager
Beanを開発者自身で用意しなかった場合、
Spring Boot はデフォルトでグローバルな AuthenticationManager
を生成します。
認可、あるいはアクセスコントロール
一度認証に成功すると、 AccessDecisionManager
を中心とした認可フェーズに移行します。フレームワークから提供される実装は3種類存在し、それぞれ AccessDecisionVoter
チェーンへと処理を移乗します。これは ProviderManager
が AuthenticationProviders
に処理を委譲するのに少し似ています。
AccessDecisionVoter
は(principal に代表される) Authentication
と、 ConfigAttributes
にデコレートされたセキュア Object
を検証します:
boolean supports(ConfigAttribute attribute); boolean supports(Class clazz); int vote(Authentication authentication, S object, Collectionattributes);
Object
は AccessDecisionManager
と AccessDecisionVoter
シグネチャにおけるジェネリックです。 - これはユーザがアクセスしたい対象すべてを表します(Java クラスのリソースやメソッドが一般的です)。 ConfigAttributes
もまたジェネリック上で扱われ、セキュア Object
や、アクセスに求められるパーミッションレベルなどのメタデータに対するデコレーションであることを表しています。 ConfigAttribute
は単なるジェネリックであり String
を返すメソッドを一つ持つインタフェース型です。この String はリソースのオーナーの意図する方法でエンコードされます。 典型的な ConfigAttribute
はユーザーロールで( ROLE_ADMIN
や ROLE_AUDIT
など )、判別のため( ROLE_
接頭辞のような)特別なフォーマットや表現を持つ場合があります。
殆どの人は AffirmativeBased
ベースのデフォルトの AccessDecisionManager
を使用します(どの voter も affirmatively を返した場合、アクセス件が付与される形式)。 どのような カスタマイズでも投票形式の処理を実装することがほとんどか、あるいは既存の朱織に新規の処理を追加したり、変更をシたりするものがほとんどです。
ConfigAttributes
では Spring Expression Language(SpEL)表現を使うのが普通です。例えば isFullyAuthenticated() && hasRole('FOO')
などです。これは AccessDecisionVoter
でサポートされており、SpEL 表現のサポートに加えてコンテキストの生成も行えます。サポートされる SpQL を拡張する場合、 SecurityExpressionRoot
に加えて必要であれば SecurityExpressionHandler
のカスタム実装が必要になります。
Web Security
Webレイヤ(UIやHTTPバックエンド)における Spring Security はサーブレットの Filters
をベースにしています。なので、まずは Filters
の役割から見ていくと良いでしょう。以下の図は単一HTTPリクエストハンドラの典型的なレイヤ分けです。
[picure]
クライアントがアプリケーションにリクエストを送ると、コントローラはリクエストURLに基づきどのフィルタ/サーブレットを適用するかを決めます。最大で一つのサーブレットが単一のリクエストをハンドルできますが、フィルターは順序をもつチェーンを構成するため、ある一つのフィルターがリクエスト自体を処理したい場合には、残りのフィルター処理を拒否することができます。また、フィルターはリクエストと、サーブレットからフィルター、そしてユーザまでに流れるレスポンスに対して変更をかけることができます。フィルターチェーンの順序はとても重要で、2つのメカニズムによってSpring Bootで管理されます:
- 1つ目は Filter
型の @Bean
が @Order
または Ordered
実装を持つことです。
- 2つ目は Filter
型の @Bean
が 自身のAPIとして順序情報を持つ FilterRegistrationBean
の派生クラスであることです。既存のフィルターは、お互いの相対的な順序関係を示すのに役立つ定数を定義しています。
(例えば、Spring Sessionの SessionRepositoryFilter
は Integer.MIN_VALUE + 50
の DEFAULT_ORDER
を持っています。これはチェーンの前段に位置することを示しているものの、それよりも早い位置にフィルターを配置できないわけではないことを示しています)
Spring Security は FilterChainProxy
具象型 Filter
としてチェーン内に導入されていますが、理由はすぐに分かります。Spring Bootアプリケーションにおいてセキュリティフィルターは ApplicationContext
内の @Bean
であり、デフォルトでインストールされます。そのため、全てのリクエストに適用されます。このフィルターはチェーン内の SecurityProperties.DEFAULT_FILTER_ORDER
の位置に挿入され、 FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER
によって固定されます(Spring Bootアプリケーションがリクエストをラップし、動作を変更した場合に、フィルターが持つと予想される最大の順序)。
それだけではありません:コンテナの観点から、Spring Security はシングルフィルターですが、内部では追加のフィルターが存在し、各々が特別な振る舞いをします。以下がそれを表した図です:
[picture]
実際、Securityのフィルターは一つ以上の間接的な層からなります。:殆どの場合、 DelegationFilterProxy
をコンテナーとし、その内部にインストールされます。このコンテナは @Bean
であり、springSecurityFilterChain
と名称を修正されます。これは フィルターチェーンとして全てのセキュリティロジックを内包した FilterChainProxy
です。これら全てのフィルターは同様のAPIを持ち(それらは全てServletのFilter
インターフェースを実装します)、残りのフィルターチェーンの処理を拒否する機会を持ちます。
Spring Security では共通のトップレベルに位置する FilterChainProxy
に管理される、コンテナーに認識されない複数のフィルターチェーンが存在する場合があります。Spring Security フィルターはフィルターチェーンのリストを含み、リクエストにマッチする最初のフィルターチェーンをディスパッチします。下図はリクエストパスマッチングに基づいたディスパッチを示しています( /foo/**
は /**
の前にマッチします)。これは極めて一般的な例ですが、リクエストマッチングの方法はこれだけではありません。ほとんどのディスパッチプロセスの重要な特徴は、一つのチェーンだけがリクエストをハンドリングするということです。
[picture]
セキュリティ構成がカスタムされていない、デフォルトのSpring Boot アプリケーションはいくつかの(この数をnとします)フィルターチェーンを持ちますが、その数は大抵はn=6です。最初(n=1)のチェーンは /css/**
や /images/**
、 /error
エラービューのような静的なリソースパターンを無視するためだけにあります( SecurityProperties
構成Beanの security.ignored
でユーザがこれらのパスを制御できます)。最後のチェーンは全てのパスにマッチし、 認証、認可、例外ハンドリング、セッションハンドリング、ヘッダー書き込みなど、よりアクティブなフィルターです。デフォルトでは11のフィルターがkのフィルターチェーンには存在しますが、どのフィルターをいつ使用すべきかをユーザが気にする必要はありません。
Note: 実際には、コンテナーがSpring Security内の全てのフィルターを知らない状態がSpring Securityアプリケーションにおいては重要であり、全ての `Filter` 型 `@Bean` はデフォルトでコンテナーに自動的に登録されます。そのため、もしカスタムフィルターをセキュリティーチェーンに追加したい場合、 `@Bean` にしないか、コンテナー登録を明示的に無効にした `FilterRegistrationBean` でラップする必要があります。
フィルターチェーンの生成とカスタマイズ
Spring Bootアプリケーションのデフォルトのフォールバックフィルターチェーンは SecurityProperties.BASIC_AUTH_ORDER
の順番で定義されています。この設定は security.basic.enabled=false
を設定するか、フォールバックのより下位層で別のルールを定義することで完全に無効にできます。 WebSecurityConfigurerAdapter
型の @Bean
を加え、 @Order
アノテーションでクラスをデコレートするだけです。
例:
@Configuration @Order(SecurityProperties.BASIC_AUTH_ORDER - 10) public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/foo/**") ...; } }
このbeanによって、Spring Securityはフォールバックの前に新しいフィルターチェーンを追加し順序付けます。
多くのアプリケーションがリソースの集合ごとにまったく異なるアクセスルールを設けています。
例えば、UIとバックエンドAPIをホストするアプリケーションでは、UIにおけるログインページへのリダイレクトと、APIにおける認証されていないリクエストに対しての401レスポンスによるトークンベースの認証を使った、クッキーベースの認証をサポートします。各リソース集合はそれぞれ自身の WebSecurityConfigurerAdapter
を持ち、それらは一意の順番とリクエストマッチャーを持ちます。もしマッチングルールが重複した場合、より早い順番のフィルターチェーンが優先されます。
伝送と認証のリクエストマッチング
セキュリティフィルターチェーン(あるいは同等の WebSecurityConfigurerAdapter
)は、そのフィルタールールをHTTPリクエストを適用するかどうかを決定する、リクエストマッチャーを持っており、一度一部のフィルターチェーンを適用することが決定した場合、他のフィルタチェーンは適用されません。しかしフィルターチェーン内では、 HttpSecurity
構成内でマッチャーを追加することで、より細かい認証のコントロールが可能になります。
例:
@Configuration @Order(SecurityProperties.BASIC_AUTH_ORDER - 10) public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/foo/**") .authorizeRequests() .antMatchers("/foo/bar").hasRole("BAR") .antMatchers("/foo/spam").hasRole("SPAM") .anyRequest().isAuthenticated(); } }
Spring Securityの構成に犯しやすい間違いの一つは、これらのリクエストマッチャーが異なるプロセスに適用されることを忘れてしまうことです。一つはフィルターチェーン全体のリクエストマッチャーと、もう一つはアクセスルールを設定するために選択されたフィルターチェーンです。
アプリケーションセキュリティルールとActuatorルールの統合
Spring Boot Actuator を管理エンドポイントに使用している場合、Actuatorをデフォルトの状態でセキュアにしたいと思うでしょう。実際、Actuatorをセキュアなアプリケーションに組み込むと、すぐにActuatorエンドポイントのためだけの追加のフィルターチェーンが適用されます。そのフィルターチェーンはActuatorエンドポイントにのみマッチするリクエストマッチャーを持ち、 デフォルトの SecurityProperties
フォールバックフィルターよりも5だけ小さい ManagementServletProperties.BASIC_AUTH_ORDER
を順番として持ちます。そのため、フォールバックフィルターよりも早く適用されます。
もしアプリケーションのセキュリティルールをActuatorエンドポイントに適用したい場合は、Actuatorのフィルターチェーンよりも早い位置にフィルターチェーンを追加し、そのフィルターチェーン内でActuatorエンドポイントを指定したリクエストマッチャーを定義することで実現できます。もしデフォルトの設定をActuatorエンドポイントに設定したい場合は、自前のフィルターをActuatorのフィルターチェーンよりもあとに、かつフォールバックよりもあとに追加します(例: ManagementServerProperties.BASIC_AUTH_ORDER + 1
)。
例:
@Configuration @Order(ManagementServerProperties.BASIC_AUTH_ORDER + 1) public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/foo/**") ...; } }
Note WebレイヤのSpring SecurityはServlet APIと紐付いているので、組み込みやその他サーブレットコンテナ上でアプリが実行される場合にのみ有効です。しかし、Spring MVC あるいはSpring Web スタックの一部に紐づいているわけではないので、例えばJAX-RSなどを使用するアプリケーションでも使用できます。
メソッドのセキュリティ
Webアプリケーションをセキュアにするように、Spring SecurityはJava メソッド実行に対するアクセスルールの適用をサポートします。Spring Securityにとって、この機能は単なる「リソース保護」の類のものです。 ユーザーの場合、アクセスルールが ConfigAttribute
文字列による共通のフォーマットによって、コードとは異なる場所で宣言されることを意味します(ロールまたは式 )。最初のステップは、例えばアプリケーショントップレベルで構成を有効化することです。
@SpringBootApplication @EnableGlobalMethodSecurity(securedEnabled = true) public class SampleSecureApplication { }
このとき直接メソッドリソースをデコレートできます。
@Service public class MyService { @Secured("ROLE_USER") public String secure() { return "Hello Security"; } }
このサンプルはセキュアメソッドを使ったサービスです。もしSpringがこの型の @Bean
を作成したら、メソッドが実際に実行される前に、メソッド処理は代替され、呼び元はセキュリティインターセプター(訳注:@Secureによるフィルター処理?)を通り抜けるでしょう。もしアクセスが拒否された場合、呼び元は実際のメソッドの戻り値の代わりに AccessDeniedException
を取得するでしょう。
セキュリティ制約を強制するために使用されるアノテーションは他にもあります。特に @PreAuthorize
や @PostAuthorize
などは、メソッドパラメータや返戻値を参照する式を記述することができます。
TIP メソッドセキュリティとWebセキュリティを一体化するのは普通ではありません。 フィルターチェーンは認証やログインページへのリダイレクトなどユーザー体験機能を提供し、メソッドセキュリティはより詳細なレベルでの保護を提供します。
スレッドとの協業
現在の認証済みプリンシパルを下流部のコンシューマー上の広範囲で利用可能にする必要があるため、Spring Security は基本的にスレッドにバインドされます。基本的なコンポーネントとして Authentication
(そしてユーザがログインしたとき、おそらく authenticated
状態にある Authentication
になります)を含む SecurityContext
があります。 SecurityContext
には SecurityContextHolder
の静的ヘルパーメソッドを経由していつでもアクセス・操作できます。これは簡潔かつ順番に ThreadLocal
を操作します:
SecurityContext context = SecurityContextHolder.getContext(); Authentication authentication = context.getAuthentication(); assert(authentication.isAuthenticated);
この方法は便利かもしれませんが、例えばカスタム認証フィルターを記述する必要がある場合などには、ユーザアプリケーションコードが認証済みユーザ情報にアクセスする方法として普通の方法ではありません( ただし、 SecurityContextHolder
を使用する必要がない場合に使用できるSpring Securityの基本クラスがもあります)。
Webエンドポイント上で現在の認証済みユーザにアクセスする必要がある場合には、 @RequestMapping
内でメソッドパラメータを使用することができます:
@RequestMapping("/foo") public String foo(@AuthenticationPrincipal User user) { ... // do stuff with user }
このアノテーションは SecurityContext
外の現在の認証情報を取得し、メソッドパラメータを生成するために getPrincipal()
メソッドを呼び出します。Authentication
内の Principal
型は認証を検証するために使用される AuthenticationManager
に依存するので、独自に定義したユーザデータへの型安全な参照を取得する便利で少しトリッキーな方法となりえます。
もしSpring Securityを使用する場合、 HttpServletRequest
の Principal
は Authentication
型となり得るため、下記のように直接使用することもできます:
@RequestMapping("/foo") public String foo(Principal principal) { Authentication authentication = (Authentication) principal; User = (User) authentication.getPrincipal(); ... // do stuff with user }
Spring Security が使用されていないとき、機能するコードを記述する必要がある場合に便利な場合があります( Authentication
クラスのローディングに関してより防御的になる必要があります )。
非同期セキュアメソッドの処理
SecurityContext
はスレッドにバインドされているため、 @Async
のようにセキュアメソッドをバックグラウンド処理として呼び出したい場合、コンテキストが伝播されることを確認する必要があります。つまり、(RunnableやCallableなどのような)バックグラウンドで実行されるタスクで SecurityContext
をラッピングする必要があるということです。Spring SecurigyはRunnableやCallableのラッパーなど、このラッピングをより簡単にするためのヘルパーを提供します。 @Async
メソッドに SecurityContext
を伝播し、 AsyncConfigurer
を用意し Executor
が正しい型であることを確認する必要があります:
@Configuration public class ApplicationConfiguration extends AsyncConfigurerSupport { @Override public Executor getAsyncExecutor() { return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5)); } }