togatttiのエンジニアメモ

過度な期待はしないでください.

LVSのDR方式の動作検証

KeepAlivedLVSを使い負荷分散、冗長化時の動作を検証する。

はじめに

負荷分散は、ダイレクトルーティング(DR)を使う。

DR(ダイレクトルーティング)では、

  1. クライアントがLVSサーバが持つVIPにリクエス
  2. LVSサーバがリアルサーバにパケット転送
  3. リアルサーバにて、パケットを処理、LVSサーバを経由せずにクライアントにレスポンス応答

という流れになる。

そのため、クライアントへの戻りのパケットは、LVSサーバを経由せず、言い換えれば、負荷をかけずにクライアントに返る。

また、LVSサーバを2台構築して、VRRPによる冗長化を行う。

後日、NAT方式も試してみたい。

構成

  • LVSサーバ
    • lvs1
      • 192.168.1.10/24
    • lvs2
      • 192.168.1.20/24
  • リアルサーバ(Nginx)
    • web1
      • 192.168.1.30/24
    • web2
      • 192.168.1.40/24
  • クライアント
    • 192.168.1.50/24

LVSサーバには、VIPとして、192.168.1.100をつける。

いずれも、Ubuntu 18.04.2で用意した仮想サーバ。

構成図

f:id:togattti1990:20190618123826p:plain
システム構成図

設定

LVSサーバ

パッケージインストール
# apt update && apt install -y ipvsadm keepalived

インストールされた以下のバージョンを使う。

# ipvsadm -v
ipvsadm v1.28 2015/02/09 (compiled with popt and IPVS v1.2.1)
# keepalived -v
Keepalived v1.3.9 (10/21,2017)

Copyright(C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>

Build options:  PIPE2 IPV4_DEVCONF LIBNL3 RTA_ENCAP RTA_EXPIRES RTA_NEWDST RTA_PREF RTA_VIA FRA_OIFNAME FRA_SUPPRESS_PREFIXLEN FRA_SUPPRESS_IFGROUP FRA_TUN_ID RTAX_CC_ALGO RTAX_QUICKACK FRA_UID_RANGE LWTUNNEL_ENCAP_MPLS LWTUNNEL_ENCAP_ILA LIBIPTC LIBIPSET_DYNAMIC LVS LIBIPVS_NETLINK IPVS_DEST_ATTR_ADDR_FAMILY IPVS_SYNCD_ATTRIBUTES IPVS_64BIT_STATS VRRP VRRP_AUTH VRRP_VMAC SOCK_NONBLOCK SOCK_CLOEXEC GLOB_BRACE OLD_CHKSUM_COMPAT FIB_ROUTING INET6_ADDR_GEN_MODE SNMP_V3_FOR_V2 SNMP SNMP_KEEPALIVED SNMP_CHECKER SNMP_RFC SNMP_RFCV2 SNMP_RFCV3 DBUS SO_MARK
iptables

新しく接続を開始する80番の入りのTCPパケットは許可する。

# iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
# iptables -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            state NEW tcp dpt:80

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
keepalivedの設定
vrrp_instance Lvsdev {
  state BACKUP
  interface ens18
  garp_master_delay 5
  virtual_router_id 1
  priority 100
  nopreempt
  advert_int 3
  virtual_ipaddress {
    192.168.1.100 dev ens18
  }
}
virtual_server 192.168.1.100 80 {
  delay_loop 3
  lb_algo rr
  lb_kind DR
  protocol TCP
  real_server 192.168.1.20 80 {
    weight 1
    inhibit_on_failure
    HTTP_GET {
      url {
        path /hello.html
        status_code 200
      }
      connect_timeout 3
      delay_before_retry 7
    }
  }
  real_server 192.168.1.30 80 {
    weight 1
    inhibit_on_failure
    HTTP_GET {
      url {
        path /
        status_code 200
      }
      connect_timeout 3
      delay_before_retry 7
    }
  }
}

keepalivedを起動する。

# systemctl start keepalived

仮想サーバテーブルを確認する。

# ipvsadm
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  lvs:http rr
  -> 192.168.1.20:http            Route   1      0          0
  -> 192.168.1.30:http            Route   1      0          0

リアルサーバ

iptables

クライアントからLVSサーバに対して行われたリクエストがリアルサーバに 転送されてきたときに、そのパケットをリアルサーバ自身のIPアドレスにリダイレクトする。

そしてクライアントに結果を返す時は、リアルサーバのルーティングテーブルを参照して、 LVSサーバ自身のIPアドレスから返しているように見せかけることでDRを実現する。

ここでは、LVSサーバには、パケットは戻らない。

iptablesを使うと

# iptables -t nat -A PREROUTING -d 192.168.1.100/32 -j REDIRECT
# iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
REDIRECT   all  --  anywhere             192.168.1.100

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

これで、LVSサーバ宛てのパケットをリアルサーバ自身にリダイレクトすることができる。

nginxのコンテンツ変更

web1

# echo hello web1 > /var/www/html/hello.html

web2

# echo hello web2 > /var/www/html/hello.html

動作検証

LVS間の冗長化

VIPが設定されているか確認する。

lvs1

$ ip -f inet addr show ens18
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 192.168.1.20/24 brd 192.168.1.255 scope global ens18
       valid_lft forever preferred_lft forever
    inet 192.168.1.100/32 scope global ens18
       valid_lft forever preferred_lft forever

lvs2

$ ip -f inet addr show ens18
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 192.168.1.30/24 brd 192.168.1.255 scope global ens18
       valid_lft forever preferred_lft forever

VIPをlvs1側だけが持っていることがわかる。

この状態で、lvs1で障害が発生したと想定してkeepalivedを落とすと、

lvs1

# systemctl stop keepalived
$ ip -f inet addr show ens18
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 192.168.1.20/24 brd 192.168.1.255 scope global ens18
       valid_lft forever preferred_lft forever

lv2

$ ip -f inet addr show ens18
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 192.168.1.30/24 brd 192.168.1.255 scope global ens18
       valid_lft forever preferred_lft forever
    inet 192.168.1.100/32 scope global ens18
       valid_lft forever preferred_lft forever

VIPがlvs2側に設定されたので、冗長化できている。

リアルサーバ間の負荷分散

lvs1で仮想サーバテーブルを確認する。

# ipvsadm -l
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  lvs1:http rr
  -> 192.168.1.20:http            Route   1      0          0
  -> 192.168.1.30:http            Route   1      0          0

クライアントから、VIPにアクセスする。

$ curl -s --connect-timeout 3 http://192.168.1.100/hello.html
hello web1
$ curl -s --connect-timeout 3 http://192.168.1.100/hello.html
hello web2
$ curl -s --connect-timeout 3 http://192.168.1.100/hello.html
hello web1
$ curl -s --connect-timeout 3 http://192.168.1.100/hello.html
hello web2

ラウンドロビンできてる。

web1のnginx落としてから、仮想サーバテーブルを確認すると 重みが0になり、振り分けの対象から除外されていることがわかる。

# ipvsadm -l
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  lvs1:http rr
  -> 192.168.1.20:http            Route   0      0          0
  -> 192.168.1.30:http            Route   1      0          0

VIPにアクセスする。

$ curl -s --connect-timeout 3 http://192.168.1.100/hello.html
hello web2
$ curl -s --connect-timeout 3 http://192.168.1.100/hello.html
hello web2
$ curl -s --connect-timeout 3 http://192.168.1.100/hello.html
hello web2
$ curl -s --connect-timeout 3 http://192.168.1.100/hello.html
hello web2

冗長化もできてる。

参考

2.5. パケット転送をオンにする - Red Hat Customer Portal

1.4.2. ダイレクトルーティング - Red Hat Customer Portal

tap dev blog - DSRとは

Swapを使っているプロセスを特定する

Swapを使っているプロセスを特定する方法をメモしておく。

使用しているOSは、Ubuntu18.04。

結論からいうと、以下でOK。

# grep VmSwap /proc/*/status | sort -n -k 2 -r

オペレーションの流れ

swapが使われていることを確認する。

# free -h
              total        used        free      shared  buff/cache   available
Mem:           7.8G        1.0G        1.9G        1.5M        4.9G        6.5G
Swap:          1.5G        748M        772M

swapを使っているプロセスを特定する。

# grep VmSwap /proc/*/status | sort -n -k 2 -r | head -5
/proc/18845/status:VmSwap:        747488 kB
/proc/20946/status:VmSwap:          8156 kB
/proc/19001/status:VmSwap:          2340 kB
/proc/20985/status:VmSwap:          1232 kB
/proc/20983/status:VmSwap:          1232 kB

原因は、netdataだった。

# ps -p 18845 uwww
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
netdata  18845  0.7  6.8 1490180 562492 ?      Ssl  Feb20 1162:59 /usr/sbin/netdata -P /var/run/netdata/netdata.pid -D -W set global process scheduling policy keep -W set global OOM score keep

Jenkinsを2系から、1系に応急的に切り戻す

バージョンが1.6系の古のJenkinsを運用していたが、誤ってapt upgradeしてしまいバージョンが 2.1系にあがり、起動しなくなってしまった。

OSは、Ubuntu 14.04。

# apt upgradeの抜粋
Unpacking jenkins (2.179) over (1.6xx)

切り戻しをしたので、メモしておく。

ただし、応急療法なので、動作性の責任は取れない。。

結論からいうと、以下の二つの対応で、切り戻しができた。

  • jenkins.warの差し替え
  • /etc/init.d/jenkinsの修正

jenkins.warの差し替え

/usr/share/jenkins配下に、2系のjenkins.warがあるので、それを元々運用していたバージョンに差し替える。

# cd /usr/share/jenkins
# mv jenkins.war jenkins.war.20190527a
# wget https://updates.jenkins-ci.org/download/war/1.6xx/jenkins.war

/etc/init.d/jenkinsの修正

修正しないと、以下のように起動スクリプトJavaのバージョン判定に引っかかり、起動できない。

# service jenkins status
Found an incorrect Java version
Java version found:
java version "1.7.0_201"
OpenJDK Runtime Environment (IcedTea 2.6.17) (7u211-2.6.17-0ubuntu0.1)
OpenJDK 64-Bit Server VM (build 24.201-b00, mixed mode)

Aborting

なので、バージョンを判定する箇所をコメントアウトする。

# diff -u /etc/init.d/jenkins.20190527a /etc/init.d/jenkins
--- /etc/init.d/jenkins.20190527a 2019-05-27 18:20:38.400852420 +0900
+++ /etc/init.d/jenkins       2019-05-27 18:16:02.159208259 +0900
@@ -59,15 +59,15 @@
 # Work out the JAVA version we are working with:
 JAVA_VERSION=$($JAVA -version 2>&1 | sed -n ';s/.* version "\(.*\)\.\(.*\)\..*".*/\1\2/p;')

-if [[ ${JAVA_ALLOWED_VERSIONS[*]} =~ "$JAVA_VERSION" ]]; then
-    echo "Correct java version found" >&2
-else
-    echo "Found an incorrect Java version" >&2
-    echo "Java version found:" >&2
-    echo $($JAVA -version) >&2
-    echo "Aborting" >&2
-    exit 1
-fi
+#if [[ ${JAVA_ALLOWED_VERSIONS[*]} =~ "$JAVA_VERSION" ]]; then
+#    echo "Correct java version found" >&2
+#else
+#    echo "Found an incorrect Java version" >&2
+#    echo "Java version found:" >&2
+#    echo $($JAVA -version) >&2
+#    echo "Aborting" >&2
+#    exit 1
+#fi

 # load environments
 if [ -r /etc/default/locale ]; then

正常に起動することを確認する。

# service jenkins start
 * Starting Jenkins Automation Server jenkins
   ...done.
# service jenkins status
Jenkins Automation Server is running with the pid 26347

AWS 認定ソリューションアーキテクトを受けてきた

結論

先月の終わりに、AWS 認定ソリューションアーキテクト - アソシエイトを受けて、合格した。

www.certmetrics.com

スペック

勉強期間

2週間。

勉強方法

www.udemy.com

をやりながら、AWSコンソールの操作、基本的なサービスを覚えた。

aws.amazon.com

ブラックベルトを一通り読んだ。

aws.koiwaclub.com

有料プランを申し込んで、全ての問題を解いた。

www.amazon.co.jp

これも2周くらい読み込んだ。

終わってみて

aws.amazon.com

AWSに関する基本知識が身につき、ケーススタディに書いてある各社のシステム構成図が読めるようになった。

自分も設計できる機会があればいいな。

次は、AWS 認定ソリューションアーキテクト - プロフェッショナルを目指したい。

Container Linuxにkubeadmを入れて、k8sクラスタを構築する

はじめに、Container Linuxを3台用意しておく。

  • k8s01(192.168.0.2/24)
  • k8s02(192.168.0.3/24)
  • k8s03(192.168.0.4/24)

バージョンは、こちら。

$ grep VERSION= /etc/os-release
VERSION=2023.5.0

Container Linuxの初期設定

各ホストで、パスワード、ホスト、固定IPアドレスの設定を済ませる。

パスワードの設定
$ sudo passwd core
ホストの名前設定
$ sudo hostnamectl set-hostname k8s01
$ sudo vi /etc/hosts
$ grep k8s01 /etc/hosts
127.0.0.1       localhost k8s01
固定IPアドレスの設定
### k8s01の設定
$ sudo vi /etc/systemd/network/static.network
$ sudo cat /etc/systemd/network/static.network
[Match]
Name=eth0

[Network]
Address=192.168.0.2/24
Gateway=192.168.0.1
DNS=8.8.8.8

### ネットワークの再起動
$ sudo systemctl restart systemd-networkd

kubeadmのインストール

kubeadmのインストール - Kubernetes

にあるContainer Linuxのインストール手順をrootで実行する。

$ cat install.sh
### CNIをインストール
CNI_VERSION="v0.6.0"
mkdir -p /opt/cni/bin
curl -L "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-amd64-${CNI_VERSION}.tgz" | tar -C /opt/cni/bin -xz
### CRICTLをインストール
CRICTL_VERSION="v1.11.1"
mkdir -p /opt/bin
curl -L "https://github.com/kubernetes-incubator/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-amd64.tar.gz" | tar -C /opt/bin -xz
### kubeadm, kubelet, kubectlをインストール
RELEASE="$(curl -sSL https://dl.k8s.io/release/stable.txt)"
mkdir -p /opt/bin
cd /opt/bin
curl -L --remote-name-all https://storage.googleapis.com/kubernetes-release/release/${RELEASE}/bin/linux/amd64/{kubeadm,kubelet,kubectl}
chmod +x {kubeadm,kubelet,kubectl}
curl -sSL "https://raw.githubusercontent.com/kubernetes/kubernetes/${RELEASE}/build/debs/kubelet.service" | sed "s:/usr/bin:/opt/bin:g" > /etc/systemd/system/kubelet.service
mkdir -p /etc/systemd/system/kubelet.service.d
curl -sSL "https://raw.githubusercontent.com/kubernetes/kubernetes/${RELEASE}/build/debs/10-kubeadm.conf" | sed "s:/usr/bin:/opt/bin:g" > /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
### kubeletを有効にする。
systemctl enable kubelet && systemctl start kubelet
$ sudo sh install.sh

k8sクラスタ構築

マスターノード構築
### k8s01側
### クラスタの初期化
$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16
### ブリッジのパケットをiptablesで処理できるようにする設定
$ sudo sysctl net.bridge.bridge-nf-call-iptables=1
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
### masterとして稼働していることを確認
$ kubectl cluster-info
Kubernetes master is running at https://10.200.8.117:6443
KubeDNS is running at https://10.200.8.117:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
### クラスタに参加するためのkubeadm join コマンドが発行されるので、控えておく。
ノード追加
### k8s02とk8s03側
$ sudo kubeadm join 192.168.0.2:6443 --token pu2npf.e42wltclx0f64bdc --discovery-token-ca-cert-hash sha256:9eb6af07398ebf79ef0acb0b70e18fca5f26c00912c2e132bbe634568316c347
クラスタ確認
### k8s01側
$ kubectl get nodes
NAME    STATUS     ROLES    AGE   VERSION
k8s01   NotReady   master   1m   v1.13.4
k8s02   NotReady   <none>   3s   v1.13.4
k8s03   NotReady   <none>   3s    v1.13.4
Flannel設定

クラスタが構築できたが、STATUSがNotReadyになっている。

ノードをまたいで、Pod同士が通信できるように、Flannelで、オーバレイネットワークを設定する。

### k8s01側
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
$ kubectl get nodes
NAME    STATUS   ROLES    AGE     VERSION
k8s01   Ready    master   5m28s   v1.13.4
k8s02   Ready    <none>   3m49s   v1.13.4
k8s03   Ready    <none>   2m23s   v1.13.4

以上で、kubeadmを用いてk8sクラスタを構築することができた。

参考

https://coreos.com/flannel/docs/latest/kubernetes.html

kubeadmを使用したシングルマスタークラスターの作成 - Kubernetes

http://sysy.livedoor.biz/archives/9791596.html