2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

OKE(Oracle Container Engine for Kubernetes)でjmeter実行環境 導入手順

Last updated at Posted at 2019-07-21

OCIのサービスであるOKE(Oracle Container Engine for Kubernetes)を使い、
jmeterクラスター環境を構築する手順です。

  • 前提
    Kubernetes実行環境はOracleLinux7で構築。
    Selinux,firewalldは無効化。

  • 今回導入する構成
    Master POD (Jmeter実行のMaster)
    Slave POD その1 (Jmeter実行のslave)
    Slave POD その2 (Jmeter実行のslave)
    influx DB (コンテナの監視取得)
    Grafana (監視をグラフにして可視化)
    上記を3つのVMで構成します。

Oracle Container Servicesにサインイン

OracleのContainer Service用リポジトリが利用できるようこちらにアクセスします。

Container Servicesをクリック。
キャプチャ.PNG

右上「Sign in」より自身のOracleアカウントでサインイン。
キャプチャ2.PNG

前準備

※rootユーザからの作業です。

dockerインストール・設定

リポジトリ設定
# vi /etc/yum.repos.d/docker.repo
以下記載

[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/oraclelinux/7
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg

インストール
# yum install docker-engine

確認
# dockerd --version

起動、自動起動
# systemctl start docker.service
# systemctl enable docker.service

Oracle Container Registryログイン
# docker login container-registry.oracle.com/kubernetes
→Oracleアカウントのユーザ名、パスワード入力

iptables設定

# iptables -P FORWARD ACCEPT
# iptables-save > /etc/sysconfig/iptables

Kubernetes環境準備

kubectlコマンド以外も使いたかったのでkubeadmをセットアップします。

インストール
# yum install kubeadm

kubeadm-setup.sh確認
# which kubeadm-setup.sh

rebootしておく
# shutdown -r now

kubeadm-setup.sh実行
# kubeadm-setup.sh up

Kubernetes操作用ユーザ作成
# useradd kubeuser

sudo設定
# echo 'kubeuser ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/kubeuser
su

切り替え
# su - kubeuser

Kubernetes の設定を読み込むように設定
$ sudo cp /etc/kubernetes/admin.conf $HOME/
$ sudo chown $(id -u):$(id -g) $HOME/admin.conf
$ ls -l $HOME/admin.conf
$ export KUBECONFIG=$HOME/admin.conf
$ echo 'export KUBECONFIG=$HOME/admin.conf' >> $HOME/.bashrc

実行確認(node確認コマンド)
$ kubectl get nodes

OCI CLI導入

インストール
$ bash -c "$(curl ?L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh)"

セットアップ
$ oci setup config

ユーザにAPI Keyを追加
$ cat ~/.oci/oci_api_key_public.pem
この公開鍵をコピーしてOCIコンソール上にて、
Identity > Users > 該当ユーザ選択 > Add Public Key > コピーした公開鍵を貼り付けてAdd

OKEクラスター構築

OCIコンソール上、左メニューよりDeveloper Services > Container Clusters(OKE)
キャプチャ3.PNG

Create Clusterを選択
キャプチャ4.PNG

設定
キャプチャ5.PNG
Cluster Creation画面で、対象項目を入力/選択

NAME:任意。今回はcluster1
KUBERNETES VERSION: 今回は最新Verのv1.12.7
QUICK CREATEを選択:必要なネットワーク周りを自動で構成

キャプチャ6.PNG SHAPE: 今回はVM.Standards1.1を選択

QUANTITY PER SUBNET:1サブネットあたりのノード数指定
PUBLIC SSH KEY: 自分の公開鍵を入力

上記入力し「create」ボタンをクリック。
Cluster StatusがActiveとなり、Node PoolsよりNodeもActiveとなれば完成です。
※デフォルトで3つのNodeを作成します。そしてデプロイに結構時間かかります。

OKEクラスター接続

作成したクラスタの「Access Kubeconfig」を選択します。
キャプチャ7.PNG
ここで2.oci~から始まるコマンドをメモに控えます。

※Kubernetes開発環境にてkubeuserで作業

既存configバックアップ
$ cp ~/admin.conf ~/.kube/config_bak1

jmeter-clusterを「.kube/config」に設定(先ほど控えたコマンドを実行します。)
$ oci ce cluster create-kubeconfig --cluster-id ocid1.cluster.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --file $HOME/.kube/config --region xx-xxxxxx-1

jmeter-clusterのconfigをバックアップ
$ cp ~/.kube/config ~/.kube/config_bak2

2つのconfigマージ
$ KUBECONFIG=~/.kube/config_bak1:~/.kube/config_bak2 kubectl config view --flatten > ~/.kube/config

環境変数修正
$ export KUBECONFIG=$HOME/.kube/config
$ vi ~/.bashrc
※以下に変更
export KUBECONFIG=$HOME/.kube/config

コンテキストのクラスタ一覧確認
$ kubectl config get-clusters
※以下のように表示される
NAME
cluster-xxxxxxxxxxx
kubernete

現在のクラスター確認 (kubernetes-admin@kubernetesが表示される)
$ kubectl config current-context

接続クラスター変更
$ kubectl config use-context "context-xxxxxxxxxxx"

現在のクラスター確認 (変更されたことを確認)
$ kubectl config current-context

クラスターにアクセスできるか確認
$ kubectl get nodes
※3ノード表示されたらOKです。

JMeterクラスター構築

今回こちらのソースを使用します。
https://github.com/kubernauts/jmeter-operator

クローン
$ git clone https://github.com/kubernauts/jmeter-kubernetes.git
$ cd jmeter-kubernetes

スクリプトに権限付与
$ chmod +x jmeter_cluster_create.sh dashboard.sh start_test.sh
 
Jmeter実行環境構築スクリプト実行
$ ./jmeter_cluster_create.sh
 
Namespaceの入力を求められるので、任意の名前入力
Enter the name of the new tenant unique name, this will be used to create the namespace
例)test
 
pod確認
$ kubectl -n test get pod -o wide
 ※5つのpodが表示されること。
NAME                              READY     STATUS    RESTARTS   AGE       IP           NODE
influxdb-jmeter-xxxxxxxxx-xxxxx   1/1       Running   0          5h        x.x.x.x      x.x.x.x
jmeter-grafana-xxxxxxxxx-xxxxx   1/1       Running   0          5h        x.x.x.x      x.x.x.x
jmeter-master-xxxxxxxxx-xxxxx    1/1       Running   0          5h        x.x.x.x      x.x.x.x
jmeter-slaves-xxxxxxxxx-xxxxx    1/1       Running   0          5h        x.x.x.x      x.x.x.x
jmeter-slaves-xxxxxxxxx-xxxxx    1/1       Running   0          5h        x.x.x.x      x.x.x.x

influxDB,Grafana設定スクリプト実行
$ ./dashboard.sh

grafana画面表示

プライベートネットワーク上で構築しているため、直接IPを指定しても接続できません。
今回ポートフォワードを使い接続します。

3001番でgrafana起動
kubectl -n test port-forward jmeter-grafana-xxxxxxxxxxx-xxxxx 3001:3000

terratermを使った例:
設定 > SSH転送より以下ポート設定
ローカルのポート: 888
リモート側のポート: 3001
キャプチャ8.PNG

ブラウザでアクセスするとgrafanaの画面が表示されます。
http://localhost:888

今回こちらのDashboardを使います。
https://grafana.com/grafana/dashboards/4026
Grafanaホーム画面 > 左メニュー「+」マークより上記のダッシュボードをインポートします。

シナリオのJMXファイルを用意

お試しで以下を使用します。

test.jmx
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.3 r1808647">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="cloussky.jmx" enabled="true">
      <stringProp name="TestPlan.comments">This test plan was created by the BlazeMeter converter v.1.1.307. Please contact support@blazemeter.com for further support.</stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header manager" enabled="true">
        <collectionProp name="HeaderManager.headers">
          <elementProp name="Accept" elementType="Header">
            <stringProp name="Header.name">Accept</stringProp>
            <stringProp name="Header.value">text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8</stringProp>
          </elementProp>
          <elementProp name="Upgrade-Insecure-Requests" elementType="Header">
            <stringProp name="Header.name">Upgrade-Insecure-Requests</stringProp>
            <stringProp name="Header.value">1</stringProp>
          </elementProp>
          <elementProp name="User-Agent" elementType="Header">
            <stringProp name="Header.name">User-Agent</stringProp>
            <stringProp name="Header.value">Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36</stringProp>
          </elementProp>
          <elementProp name="Accept-Encoding" elementType="Header">
            <stringProp name="Header.name">Accept-Encoding</stringProp>
            <stringProp name="Header.value">gzip, deflate, br</stringProp>
          </elementProp>
        </collectionProp>
      </HeaderManager>
      <hashTree/>
      <Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
        <collectionProp name="Arguments.arguments">
          <elementProp name="BASE_URL_1" elementType="Argument">
            <stringProp name="Argument.name">BASE_URL_1</stringProp>
            <stringProp name="Argument.value">xxxxx.jp</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
        </collectionProp>
      </Arguments>
      <hashTree/>
      <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults" enabled="true">
        <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true">
          <collectionProp name="Arguments.arguments"/>
        </elementProp>
        <stringProp name="HTTPSampler.domain"></stringProp>
        <stringProp name="HTTPSampler.port"></stringProp>
        <stringProp name="HTTPSampler.protocol"></stringProp>
        <stringProp name="HTTPSampler.contentEncoding"></stringProp>
        <stringProp name="HTTPSampler.path"></stringProp>
        <boolProp name="HTTPSampler.image_parser">true</boolProp>
        <boolProp name="HTTPSampler.concurrentDwn">true</boolProp>
        <stringProp name="HTTPSampler.concurrentPool">6</stringProp>
        <stringProp name="HTTPSampler.connect_timeout"></stringProp>
        <stringProp name="HTTPSampler.response_timeout"></stringProp>
      </ConfigTestElement>
      <hashTree/>
      <DNSCacheManager guiclass="DNSCachePanel" testclass="DNSCacheManager" testname="DNS Cache Manager" enabled="true">
        <collectionProp name="DNSCacheManager.servers"/>
        <boolProp name="DNSCacheManager.clearEachIteration">true</boolProp>
        <boolProp name="DNSCacheManager.isCustomResolver">false</boolProp>
      </DNSCacheManager>
      <hashTree/>
      <AuthManager guiclass="AuthPanel" testclass="AuthManager" testname="HTTP Authorization Manager" enabled="true">
        <collectionProp name="AuthManager.auth_list"/>
      </AuthManager>
      <hashTree/>
      <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true">
        <collectionProp name="CookieManager.cookies"/>
        <boolProp name="CookieManager.clearEachIteration">true</boolProp>
      </CookieManager>
      <hashTree/>
      <CacheManager guiclass="CacheManagerGui" testclass="CacheManager" testname="HTTP Cache Manager" enabled="true">
        <boolProp name="clearEachIteration">true</boolProp>
        <boolProp name="useExpires">false</boolProp>
      </CacheManager>
      <hashTree/>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <stringProp name="LoopController.loops">100</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">2</stringProp>
        <stringProp name="ThreadGroup.ramp_time">2</stringProp>
        <longProp name="ThreadGroup.start_time">1363247040000</longProp>
        <longProp name="ThreadGroup.end_time">1363247040000</longProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration">0</stringProp>
        <stringProp name="ThreadGroup.delay">0</stringProp>
      </ThreadGroup>
      <hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="toppage" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain">${BASE_URL_1}</stringProp>
          <stringProp name="HTTPSampler.port"></stringProp>
          <stringProp name="HTTPSampler.protocol">https</stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path"></stringProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="Constant Timer" enabled="true">
            <stringProp name="ConstantTimer.delay">0</stringProp>
          </ConstantTimer>
          <hashTree/>
        </hashTree>
        <BackendListener guiclass="BackendListenerGui" testclass="BackendListener" testname="Backend Listener" enabled="true">
          <elementProp name="arguments" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" enabled="true">
            <collectionProp name="Arguments.arguments">
              <elementProp name="influxdbMetricsSender" elementType="Argument">
                <stringProp name="Argument.name">influxdbMetricsSender</stringProp>
                <stringProp name="Argument.value">org.apache.jmeter.visualizers.backend.influxdb.HttpMetricsSender</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="influxdbUrl" elementType="Argument">
                <stringProp name="Argument.name">influxdbUrl</stringProp>
                <stringProp name="Argument.value">http://jmeter-influxdb:8086/write?db=jmeter</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="application" elementType="Argument">
                <stringProp name="Argument.name">application</stringProp>
                <stringProp name="Argument.value">xxxxx</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="measurement" elementType="Argument">
                <stringProp name="Argument.name">measurement</stringProp>
                <stringProp name="Argument.value">jmeter</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="summaryOnly" elementType="Argument">
                <stringProp name="Argument.name">summaryOnly</stringProp>
                <stringProp name="Argument.value">false</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="samplersRegex" elementType="Argument">
                <stringProp name="Argument.name">samplersRegex</stringProp>
                <stringProp name="Argument.value">.*</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="percentiles" elementType="Argument">
                <stringProp name="Argument.name">percentiles</stringProp>
                <stringProp name="Argument.value">90;95;99</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="testTitle" elementType="Argument">
                <stringProp name="Argument.name">testTitle</stringProp>
                <stringProp name="Argument.value">Test name</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="eventTags" elementType="Argument">
                <stringProp name="Argument.name">eventTags</stringProp>
                <stringProp name="Argument.value"></stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="classname">org.apache.jmeter.visualizers.backend.influxdb.InfluxdbBackendListenerClient</stringProp>
        </BackendListener>
        <hashTree/>
      </hashTree>
    </hashTree>
    <WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="WorkBench" enabled="true">
      <boolProp name="WorkBench.save">true</boolProp>
    </WorkBench>
    <hashTree/>
  </hashTree>
</jmeterTestPlan>

※内容について
LoopControler.loops: 100
⇒とりあえず100回にしています。
ThreadGroup.num_threads: 2
ThreadGroup.ramp_time:
⇒1秒間に2ユーザアクセスするようにしています。
⇒「xxxxx」という箇所があるのでアクセスしたいドメイン名に変更してください。

JMeterでテスト実行

あとはjmeter実行スクリプト(start_test.sh)の引数にシナリオのjmxファイルを指定して実行するだけです。

./start_test.sh test.jmx

Master POD→各Slave PODにjmxファイルの内容が送信され実行されます。
Grafana上でメトリクスが表示されます。

※Jmeter実行時のグラフイメージ
キャプチャ9.PNG

あとはログも確認しましょう。

Slave PODの数を増やしたい場合

クローンしたフォルダ内にあるjmeter_slaves_deploy.yamlを編集します。
設定ファイル内、"replicas"の数を変更すれば簡単にSlave PODの数が増減できます。
現在Slave PODは2つのため3つに増やしてみます。

編集
$ vi jmeter_slaves_deploy.yaml
 
※replicasを3に変更
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: jmeter-slaves
  labels:
    jmeter_mode: slave
spec:
  replicas: 3

再デプロイ
$ kubectl -n test apply -f jmeter_slaves_deploy.yaml

確認
$ kubectl -n test get pod -o wide
※jmeter-slavesで始まるPODが3つ存在し、StatusがrunnningになればOKです。

長い記事になったので最後のほうどんどん適当になってしまいました…

PODの数を簡単に変更できるのは便利だと思います。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?