ビビリフクロウの足跡

いつもお世話になっているインターネットへの恩返し

Kubernetes環境にDashboardとDNSサービスをデプロイする

前回作成したマルチマスターKubernetes環境はKubernetesの最小構成なので、今回はDashboardDNSサービスをデプロイしたいと思います。 まずはDashboardのデプロイから。

Dashboardの設定

  • kube-controller-managerの設定で、ServiceAccountがAPIサーバとの通信に使うルートCA証明書を持つように設定する
# sed -i 's@KUBE_CONTROLLER_MANAGER_ARGS="@KUBE_CONTROLLER_MANAGER_ARGS="--root-ca-file=/etc/kubernetes/certs/apiserver.crt @g' /etc/kubernetes/controller-manager
# systemctl restart kube-controller-manager
  • 既存のServiceAccount「default」を削除(自動的に新しいCA証明書を持つServiceAccountが再作成されます)
# kubectl delete sa default
# kubectl delete sa default -n kube-system
# kubectl delete sa default -n kube-public
  • APIサーバ側でsystem:anonymousがNamespace"kube-system"のResource"*"にRW権限を付ける
    • ABACの設定
# vi /etc/kubernetes/abac-policy.json
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"system:anonymous", "namespace": "kube-system", "resource": "*"}}
  • APIサーバの認可機能で、RBACも適用するように設定する。
# sed -i 's/--authorization-mode=ABAC/--authorization-mode=RBAC,ABAC/g' /etc/kubernetes/apiserver
# systemctl restart kube-apiserver
  • manifestを落としてくる
# curl -O https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
  • manifestの修正
    • Dashboard自体のベーシック認証を有効化する
      • オプション指定
...
    spec:
      containers:
      - name: kubernetes-dashboard
        image: k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3
        ports:
        - containerPort: 8443
          protocol: TCP
        args:
          - --auto-generate-certificates
          - --authentication-mode     # ここを追加
          - basic                     # ここを追加
          # Uncomment the following line to manually specify Kubernetes API server Host
          # If not specified, Dashboard will attempt to auto discover the API server and connect
          # to it. Uncomment only if the default does not work.
          # - --apiserver-host=http://my-address:port
...
  • kubernetes-dashboardポッドのデプロイ
# kubectl create -f kubernetes-dashboard.yaml

次にDNSサービスのデプロイですねー

kube-dns設定

  • kubeletにkube-dnsサービスのIPと管理ドメインを設定する(kube-*)
# sed -i 's/KUBELET_ARGS="/KUBELET_ARGS="--cluster-dns=172.16.0.2 --cluster-domain=cluster.local /g' /etc/kubernetes/kubelet
# systemctl restart kubelet
  • manifestをダウンロード
# curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/kube-dns.yaml.sed
# mv kube-dns.yaml.sed kube-dns.yaml
  • manifestを修正して、以下を設定
# sed -i "s/\$DNS_SERVER_IP/172.16.0.2/g" kube-dns.yaml
# sed -i "s/\$DNS_DOMAIN/cluster.local/g" kube-dns.yaml
  • kube-dnsポッドのデプロイ
# kubectl create -f kube-dns.yaml

これでDashboardDNSサービス用のPodなりServiceなりが正常に上がるはずです。

Dashboardはこんな感じ。

f:id:bbrfkr:20180325202055p:plain

DNSサービスの稼働確認をするために、httpdポッドを二つ、別のノードに上げてみましょう。Masterノードで以下を叩きます。

# kubectl create deploy --image httpd httpd-1
# kubectl create deploy --image httpd httpd-2
# kubectl expose deploy httpd-1 --port 80
# kubectl expose deploy httpd-2 --port 80

ポッドが上がったら、以下のコマンドでcurlをそれぞれのポッドにインストールします。

# kubectl exec <httpd-1のポッド> -- apt-get update 
# kubectl exec <httpd-1のポッド> -- apt-get install -y curl
# kubectl exec <httpd-2のポッド> -- apt-get update
# kubectl exec <httpd-2のポッド> -- apt-get install -y curl

curlをインストールしたら、互いのサービスに対して名前解決ができるかを確かめます。

# kubectl exec <httpd-1のポッド> -- curl httpd-2
# kubectl exec <httpd-2のポッド> -- curl httpd-1

複数マスターを持つ高可用性Kubernetesクラスタを作る

先日、keepalivedを用いてhaproxyを冗長化する記事を上げましたが、この高可用性ロードバランサを用いて複数マスターを持つ高可用性Kubernetestクラスタを作ってみたので、その手順を公開いたします。

環境

アーキテクチャはシンプルで、kubeletやkubectlのAPIリクエストを直接マスターに送るのではなく、間にhaproxyを挟むことによってマスターの冗長化を実現しています。

f:id:bbrfkr:20180318233148p:plain

なお、利用OSはCentOS 7.4になります。

手順

以降、実際の手順です。

共通設定(haproxy-01, haproxy-02, kube-master-01, kube-master-02, kube-master-03, kube-node-01, kube-node-02)

  • hosts設定
# vi /etc/hosts
192.168.0.82  kube-master-01.bbrfkr.mydns.jp
192.168.0.83  kube-master-02.bbrfkr.mydns.jp
192.168.0.84  kube-master-03.bbrfkr.mydns.jp
192.168.0.85  kube-node-01.bbrfkr.mydns.jp
192.168.0.86  kube-node-02.bbrfkr.mydns.jp
192.168.0.87  haproxy-01.bbrfkr.mydns.jp
192.168.0.88  haproxy-02.bbrfkr.mydns.jp

検証のため、無効化します。

# sed -i 's/SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config
# systemctl disable firewalld
# reboot

LBの構築(haproxy-*)

  • haproxy、keepalivedのインストール(haproxy-01, haproxy-02)
# yum -y install haproxy keepalived
  • haproxyの設定(haproxy-01, haproxy-02)
# vi /etc/haproxy/haproxy.cfg
frontend api *:6443
    default_backend    api

frontend etcd *:2379
    default_backend    etcd

backend api
    balance   leastconn
    server    master1  kube-master-01.bbrfkr.mydns.jp:6443  check
    server    master2  kube-master-02.bbrfkr.mydns.jp:6443  check  backup
    server    master3  kube-master-03.bbrfkr.mydns.jp:6443  check  backup

backend etcd
    balance   leastconn
    server    master1  kube-master-01.bbrfkr.mydns.jp:2379  check
    server    master2  kube-master-02.bbrfkr.mydns.jp:2379  check  backup
    server    master3  kube-master-03.bbrfkr.mydns.jp:2379  check  backup
  • haproxyの起動と自動起動設定(haproxy-01, haproxy-02)
# systemctl enable haproxy
# systemctl start haproxy
# vi /etc/keepalived/keepalived.conf
vrrp_script chk_haproxy {
    script "systemctl is-active haproxy"
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 1
    priority 101
    virtual_ipaddress {
        192.168.0.89
    }
    track_script {
        chk_haproxy
    }
}
# vi /etc/keepalived/keepalived.conf
vrrp_script chk_haproxy {
    script "systemctl is-active haproxy"
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 1
    priority 100
    virtual_ipaddress {
        192.168.0.89
    }
    track_script {
        chk_haproxy
    }
}
# systemctl enable keepalived
# systemctl start keepalived

Dockerのインストール(kube-*)

  • パッケージのインストール
# yum -y install docker
  • コンテナボリューム用ディスクのセットアップ
# cat <<EOF > /etc/sysconfig/docker-storage-setup
DEVS=/dev/vda
VG=docker-vg
EOF
# docker-storage-setup
  • ヘアピンNATの有効化(これをしないと、Podが自分自身のServiceにアクセスできなくなる...)
# sed -i "s/OPTIONS='/OPTIONS='--userland-proxy=false /g" /etc/sysconfig/docker
# systemctl enable docker
# systemctl start docker

etcdのインストール(kube-master-*)

  • パッケージのインストール(kube-master-*)
# yum -y install etcd
  • 設定ファイルの書き換え(kube-master-01)
# vi /etc/etcd/etcd.conf
#[Member]
ETCD_NAME=kube-master-01
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.0.82:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.0.82:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.0.82:2379"
ETCD_INITIAL_CLUSTER="kube-master-01=http://192.168.0.82:2380,kube-master-02=http://192.168.0.83:2380,kube-master-03=http://192.168.0.84:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster-for-kubernetes"
  • 設定ファイルの書き換え(kube-master-02)
# vi /etc/etcd/etcd.conf
#[Member]
ETCD_NAME=kube-master-02
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.0.83:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.0.83:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.0.83:2379"
ETCD_INITIAL_CLUSTER="kube-master-01=http://192.168.0.82:2380,kube-master-02=http://192.168.0.83:2380,kube-master-03=http://192.168.0.84:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster-for-kubernetes"
  • 設定ファイルの書き換え(kube-master-03)
# vi /etc/etcd/etcd.conf
#[Member]
ETCD_NAME=kube-master-03
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.0.84:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.0.84:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.0.84:2379"
ETCD_INITIAL_CLUSTER="kube-master-01=http://192.168.0.82:2380,kube-master-02=http://192.168.0.83:2380,kube-master-03=http://192.168.0.84:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster-for-kubernetes"
  • サービスの起動と自動起動設定(kube-master-02, kube-master-03)
# systemctl enable etcd
# systemctl start etcd
  • サービスの起動と自動起動設定(kube-master-01)
# systemctl enable etcd
# systemctl start etcd
# etcdctl member list
# etcdctl cluster-health
  • コンテナ用ネットワーク設定作成(kube-master-01)
# etcdctl mkdir /ha-kubernetes/network
# etcdctl mk /ha-kubernetes/network/config '{ "Network": "10.1.0.0/16", "SubnetLen": 24, "Backend": { "Type": "vxlan" } }'

flannelのインストール - master編 - (kube-master-*)

  • wgetのインストール
# yum -y install wget
  • バイナリのダウンロード・展開と配置
# mkdir flanneld && cd flanneld
# wget https://github.com/coreos/flannel/releases/download/v0.10.0/flannel-v0.10.0-linux-amd64.tar.gz
# tar xvzf flannel-v0.10.0-linux-amd64.tar.gz
# mv flanneld mk-docker-opts.sh /usr/bin
  • ごみ掃除
# cd ..
# rm -rf flanneld
  • systemdファイル作成
# vi /usr/lib/systemd/system/flanneld.service
[Unit]
Description=Flanneld overlay address etcd agent
After=network.target
After=network-online.target
Wants=network-online.target
After=etcd.service
Before=docker.service

[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/flanneld
EnvironmentFile=-/etc/sysconfig/docker-network
ExecStart=/usr/bin/flanneld-start $FLANNEL_OPTIONS
ExecStartPost=/usr/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker
Restart=on-failure

[Install]
WantedBy=multi-user.target
RequiredBy=docker.service
# touch /usr/bin/flanneld-start
# chown root:root /usr/bin/flanneld-start
# chmod 755 /usr/bin/flanneld-start
# vi /usr/bin/flanneld-start
#!/bin/sh

exec /usr/bin/flanneld \
        -etcd-endpoints=${FLANNEL_ETCD_ENDPOINTS:-${FLANNEL_ETCD}} \
        -etcd-prefix=${FLANNEL_ETCD_PREFIX:-${FLANNEL_ETCD_KEY}} \
        "$@"
# mkdir /usr/lib/systemd/system/docker.service.d
# vi /usr/lib/systemd/system/docker.service.d/flannel.conf
[Service]
EnvironmentFile=-/run/flannel/docker
  • 設定ファイルの編集
# vi /etc/sysconfig/flanneld
FLANNEL_ETCD_ENDPOINTS="http://127.0.0.1:2379"
FLANNEL_ETCD_PREFIX="/ha-kubernetes/network"
#FLANNEL_OPTIONS=""
# systemctl enable flanneld
# systemctl start flanneld
  • dockerサービス再起動
# systemctl restart docker

flannelのインストール - node編 - (kube-node-*)

  • wgetのインストール
# yum -y install wget
  • バイナリのダウンロード・展開と配置
# mkdir flanneld && cd flanneld
# wget https://github.com/coreos/flannel/releases/download/v0.10.0/flannel-v0.10.0-linux-amd64.tar.gz
# tar xvzf flannel-v0.10.0-linux-amd64.tar.gz
# mv flanneld mk-docker-opts.sh /usr/bin
  • ごみ掃除
# cd ..
# rm -rf flanneld
  • systemdファイル作成
# vi /usr/lib/systemd/system/flanneld.service
[Unit]
Description=Flanneld overlay address etcd agent
After=network.target
After=network-online.target
Wants=network-online.target
After=etcd.service
Before=docker.service

[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/flanneld
EnvironmentFile=-/etc/sysconfig/docker-network
ExecStart=/usr/bin/flanneld-start $FLANNEL_OPTIONS
ExecStartPost=/usr/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker
Restart=on-failure

[Install]
WantedBy=multi-user.target
RequiredBy=docker.service
# touch /usr/bin/flanneld-start
# chown root:root /usr/bin/flanneld-start
# chmod 755 /usr/bin/flanneld-start
# vi /usr/bin/flanneld-start
#!/bin/sh

exec /usr/bin/flanneld \
        -etcd-endpoints=${FLANNEL_ETCD_ENDPOINTS:-${FLANNEL_ETCD}} \
        -etcd-prefix=${FLANNEL_ETCD_PREFIX:-${FLANNEL_ETCD_KEY}} \
        "$@"
# mkdir /usr/lib/systemd/system/docker.service.d
# vi /usr/lib/systemd/system/docker.service.d/flannel.conf
[Service]
EnvironmentFile=-/run/flannel/docker
  • 設定ファイルの編集
# vi /etc/sysconfig/flanneld
FLANNEL_ETCD_ENDPOINTS="http://192.168.0.89:2379"
FLANNEL_ETCD_PREFIX="/ha-kubernetes/network"
#FLANNEL_OPTIONS=""
# systemctl enable flanneld
# systemctl start flanneld
  • dockerサービス再起動
# systemctl restart docker

kubernetesの設定 - master編 - (kube-master-*)

  • バイナリのダウンロード
# curl -O https://storage.googleapis.com/kubernetes-release/release/v1.9.1/kubernetes-server-linux-amd64.tar.gz
  • tarボールの展開
# tar xvzf kubernetes-server-linux-amd64.tar.gz
  • 不要物の削除
# rm -f kubernetes/server/bin/*.*
  • バイナリの移動
# mv kubernetes/server/bin/* /usr/bin/
  • ゴミ掃除
# rm -rf kubernetes*
  • kubeユーザの作成
# groupadd --system kube
# useradd \
      --home-dir "/home/kube" \
      --system \
      --shell /bin/false \
      -g kube \
      kube
# mkdir -p /home/kube
# chown kube:kube /home/kube
# mkdir -p /etc/kubernetes
# chown kube:kube /etc/kubernetes
  • master用systemdファイル配置
# vi /usr/lib/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
After=etcd.service

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/apiserver
User=kube
ExecStart=/usr/bin/kube-apiserver \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBE_ETCD_SERVERS \
            $KUBE_API_PORT \
            $KUBE_API_ADDRESS \
            $KUBE_ALLOW_PRIV \
            $KUBE_SERVICE_ADDRESSES \
            $KUBE_ADMISSION_CONTROL \
            $KUBE_API_ARGS
Restart=on-failure
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
# vi /usr/lib/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/controller-manager
User=kube
ExecStart=/usr/bin/kube-controller-manager \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBE_CONTROLLER_MANAGER_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
# vi /usr/lib/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler Plugin
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/scheduler
User=kube
ExecStart=/usr/bin/kube-scheduler \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBE_SCHEDULER_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
  • master用設定ファイル配置
# vi /etc/kubernetes/config
###
# kubernetes system config
#
# The following values are used to configure various aspects of all
# kubernetes services, including
#
#   kube-apiserver.service
#   kube-controller-manager.service
#   kube-scheduler.service
#   kubelet.service
#   kube-proxy.service
# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true"

# journal message level, 0 is debug
KUBE_LOG_LEVEL="--v=0"

# Should this cluster be allowed to run privileged docker containers
KUBE_ALLOW_PRIV="--allow-privileged=true"
# vi /etc/kubernetes/apiserver
###
# kubernetes system config
#
# The following values are used to configure the kube-apiserver
#

# The address on the local server to listen to.
KUBE_API_ADDRESS="--insecure-bind-address=127.0.0.1 --bind-address=0.0.0.0"

# The port on the local server to listen on.
KUBE_API_PORT="--insecure-port=8080 --secure-port=6443"

# Comma separated list of nodes in the etcd cluster
KUBE_ETCD_SERVERS="--etcd-servers=http://127.0.0.1:2379"

# Address range to use for services
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=172.16.0.0/16"

# default admission control policies
KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota"

# Add your own!
KUBE_API_ARGS="--apiserver-count 3 --cert-dir /etc/kubernetes/certs --basic-auth-file=/etc/kubernetes/basic-auth-list.csv --authorization-mode=ABAC --authorization-policy-file=/etc/kubernetes/abac-policy.json"
# vi /etc/kubernetes/controller-manager
###
# The following values are used to configure the kubernetes controller-manager

# defaults from config and apiserver should be adequate

# Add your own!
KUBE_CONTROLLER_MANAGER_ARGS="--kubeconfig=/etc/kubernetes/kubeconfig --service-account-private-key-file=/etc/kubernetes/certs/apiserver.key"
# vi /etc/kubernetes/scheduler
###
# kubernetes scheduler config

# default config should be adequate

# Add your own!
KUBE_SCHEDULER_ARGS="--kubeconfig=/etc/kubernetes/kubeconfig"
# vi /etc/kubernetes/kubeconfig
apiVersion: v1
kind: Config
clusters:
  - cluster:
      server: https://127.0.0.1:6443
      insecure-skip-tls-verify: true
    name: ha-kubernetes
contexts:
  - context:
      cluster: ha-kubernetes
      user: kube-process
    name: kube-process-to-ha-kubernetes
current-context: kube-process-to-ha-kubernetes
users:
  - name: kube-process
    user:
      username: kube-process
      password: password
# vi /etc/kubernetes/abac-policy.json
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group":"system:authenticated",  "nonResourcePath": "*", "readonly": true}}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group":"system:unauthenticated", "nonResourcePath": "*", "readonly": true}}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"admin",     "namespace": "*",              "resource": "*",         "apiGroup": "*"                   }}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"kube-process",     "namespace": "*",              "resource": "*",         "apiGroup": "*"                   }}
# vi /etc/kubernetes/basic-auth-list.csv
password,admin,1
password,kube-process,2
  • 設定ファイル権限変更
# chown kube:kube /etc/kubernetes/*
  • パスワードファイルセキュリティ対策
# chmod 600 /etc/kubernetes/basic-auth-list.csv
  • OpenSSLの設定ファイル「openssl.cnf」のコピー(kube-master-01のみ)
# cd
# cp /etc/pki/tls/openssl.cnf .
  • openssl.cnfにSANの設定を入れる(kube-master-01のみ)
# sed -i 's/\[ v3_ca \]/\[ v3_ca \]\nsubjectAltName = @alt_names/g' openssl.cnf
# cat <<EOF >> openssl.cnf
[ alt_names ]
DNS.1 = kubernetes.default.svc
DNS.2 = kubernetes.default
DNS.3 = kubernetes
DNS.4 = localhost
IP.1 = 192.168.0.89
IP.2 = 172.16.0.1
EOF
  • APIサーバ用鍵の作成(kube-master-01のみ)
# openssl genrsa 2048 > apiserver.key
  • CSRの作成(kube-master-01のみ)
# openssl req -new -key apiserver.key -out apiserver.csr
# openssl x509 -days 7305 -req -signkey apiserver.key -extensions v3_ca -extfile ./openssl.cnf -in apiserver.csr -out apiserver.crt
# cp apiserver* /etc/kubernetes/certs
# scp apiserver* 192.168.0.83:/etc/kubernetes/certs
# scp apiserver* 192.168.0.84:/etc/kubernetes/certs
# chmod 600 /etc/kubernetes/certs/apiserver.key
# chown kube:kube /etc/kubernetes/certs/apiserver*
  • ごみ掃除(kube-master-01のみ)
# rm -f apiserver.*
# rm -f openssl.cnf
# systemctl enable kube-apiserver kube-scheduler kube-controller-manager
# systemctl start kube-apiserver
# systemctl start kube-scheduler kube-controller-manager

kubernetesの設定 - node編 1 - (kube-master-*)

  • swapの無効化
# vi /etc/fstab
# reboot
# mkdir -p /var/lib/kubelet
# chown root:root /var/lib/kubelet
  • node用systemdファイル配置
# vi /usr/lib/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/var/lib/kubelet
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/kubelet
ExecStart=/usr/bin/kubelet \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBELET_ADDRESS \
            $KUBELET_PORT \
            $KUBELET_HOSTNAME \
            $KUBE_ALLOW_PRIV \
            $KUBELET_POD_INFRA_CONTAINER \
            $KUBELET_ARGS
Restart=on-failure

[Install]
WantedBy=multi-user.target
# vi /usr/lib/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/proxy
ExecStart=/usr/bin/kube-proxy \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBE_PROXY_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
  • node用設定ファイル配置
# vi /etc/kubernetes/kubelet
###
# kubernetes kubelet (minion) config

# The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)
KUBELET_ADDRESS="--address=0.0.0.0"

# The port for the info server to serve on
KUBELET_PORT="--port=10250"

# You may leave this blank to use the actual hostname
# KUBELET_HOSTNAME="--hostname-override=127.0.0.1"
KUBELET_HOSTNAME=""

# pod infrastructure container
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=gcr.io/google_containers/pause-amd64:3.0"

# Add your own!
KUBELET_ARGS="--kubeconfig=/etc/kubernetes/kubeconfig --cgroup-driver=systemd --runtime-cgroups=/systemd/system.slice --kubelet-cgroups=/systemd/system.slice"
# vi /etc/kubernetes/proxy
###
# kubernetes proxy config

# default config should be adequate

# Add your own!
KUBE_PROXY_ARGS="--kubeconfig=/etc/kubernetes/kubeconfig --cluster-cidr=172.16.0.0/16"
  • 設定ファイル権限変更
# chown kube:kube /etc/kubernetes/*
# systemctl enable kubelet kube-proxy
# systemctl start kubelet kube-proxy

kubernetesの設定 - node編 2 - (kube-node-*)

  • バイナリのダウンロード
# curl -O https://storage.googleapis.com/kubernetes-release/release/v1.9.1/kubernetes-node-linux-amd64.tar.gz
  • tarボールの展開
# tar xvzf kubernetes-node-linux-amd64.tar.gz
  • 不要物の削除
# rm -f kubernetes/node/bin/*.*
  • バイナリの移動
# mv kubernetes/node/bin/* /usr/bin/
  • ゴミ掃除
# rm -rf kubernetes*
  • swapの無効化(node)
# vi /etc/fstab
# reboot
  • kubeユーザの作成
# groupadd --system kube
# useradd --home-dir "/home/kube" \
      --system \
      --shell /bin/false \
      -g kube \
      kube
# mkdir -p /home/kube
# chown kube:kube /home/kube
# mkdir -p /var/lib/kubelet
# chown root:root /var/lib/kubelet
# mkdir -p /etc/kubernetes
# chown kube:kube /etc/kubernetes
  • node用systemdファイル配置
# vi /lib/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/var/lib/kubelet
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/kubelet
ExecStart=/usr/bin/kubelet \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBELET_ADDRESS \
            $KUBELET_PORT \
            $KUBELET_HOSTNAME \
            $KUBE_ALLOW_PRIV \
            $KUBELET_POD_INFRA_CONTAINER \
            $KUBELET_ARGS
Restart=on-failure

[Install]
WantedBy=multi-user.target
# vi /lib/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/proxy
ExecStart=/usr/bin/kube-proxy \
            $KUBE_LOGTOSTDERR \
            $KUBE_LOG_LEVEL \
            $KUBE_PROXY_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
  • node用設定ファイル配置
# vi /etc/kubernetes/config
###
# kubernetes system config
#
# The following values are used to configure various aspects of all
# kubernetes services, including
#
#   kube-apiserver.service
#   kube-controller-manager.service
#   kube-scheduler.service
#   kubelet.service
#   kube-proxy.service
# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true"

# journal message level, 0 is debug
KUBE_LOG_LEVEL="--v=0"

# Should this cluster be allowed to run privileged docker containers
KUBE_ALLOW_PRIV="--allow-privileged=true"
# vi /etc/kubernetes/kubelet
###
# kubernetes kubelet (minion) config

# The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)
KUBELET_ADDRESS="--address=0.0.0.0"

# The port for the info server to serve on
KUBELET_PORT="--port=10250"

# You may leave this blank to use the actual hostname
# KUBELET_HOSTNAME="--hostname-override=127.0.0.1"
KUBELET_HOSTNAME=""

# pod infrastructure container
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=gcr.io/google_containers/pause-amd64:3.0"

# Add your own!
KUBELET_ARGS="--kubeconfig=/etc/kubernetes/kubeconfig --cgroup-driver=systemd --runtime-cgroups=/systemd/system.slice --kubelet-cgroups=/systemd/system.slice"
# vi /etc/kubernetes/proxy
###
# kubernetes proxy config

# default config should be adequate

# Add your own!
KUBE_PROXY_ARGS="--kubeconfig=/etc/kubernetes/kubeconfig --cluster-cidr=172.16.0.0/16"
# vi /etc/kubernetes/kubeconfig
apiVersion: v1
kind: Config
clusters:
  - cluster:
      server: https://192.168.0.89:6443
      insecure-skip-tls-verify: true
    name: ha-kubernetes
contexts:
  - context:
      cluster: ha-kubernetes
      user: kube-process
    name: kube-process-to-ha-kubernetes
current-context: kube-process-to-ha-kubernetes
users:
  - name: kube-process
    user:
      username: kube-process
      password: password
  • 設定ファイル権限変更
# chown kube:kube /etc/kubernetes/*
# systemctl enable kubelet kube-proxy
# systemctl start kubelet kube-proxy

haproxyをkeepalivedで冗長化して高可用性ロードバランサを構築する

ロードバランサとして活用できるhaproxyですが、ロードバランサ冗長性確保用デーモンであるkeepalivedを使って冗長化してみたので、その手法をメモしておきます。

なんでいきなりhaproxy + keepalivedかというと、Kubernetesの冗長化においてkubeletやkubectlからのkube-apiserverやetcdへのアクセスはMasterノードが複数になるためロードバランスしてやらなくてなりません。ですのでロードバランサが必須になるんですが、単純にロードバランサを追加するだけだとロードバランサ自体がSPOFになってしまうので、ロードバランサ自体の高可用性も勉強してみた、という形です。

構成

構成としては以下の通りとします。

  • Webサーバ
    • IP: 192.168.0.60, ホスト名: apache-01.bbrfkr.mydns.jp
    • IP: 192.168.0.61, ホスト名: apache-02.bbrfkr.mydns.jp
  • ロードバランサ
    • IP: 192.168.0.62, ホスト名: haproxy-01.bbrfkr.mydns.jp
    • IP: 192.168.0.63, ホスト名: haproxy-01.bbrfkr.mydns.jp
    • VIP: 192.168.0.64

VIP: 192.168.0.64へのHTTPアクセスを背後のhaproxy-01, haproxy-02が受け付け、haproxyはさらに背後のapache-01, apache-02にリクエストを流す、という単純な構成です。

構築手順

  • SELinux、firewalldの無効化(haproxy-01, haproxy-02)

検証のため、無効化します。

# sed -i 's/SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config
# systemctl disable firewalld
# reboot
  • haproxy、keepalivedのインストール(haproxy-01, haproxy-02)
# yum -y install haproxy keepalived
  • haproxyの設定(haproxy-01, haproxy-02)
# vi /etc/haproxy/haproxy.cfg
frontend main *:80
    default_backend    web

backend web
    balance    roundrobin
    server    web1  192.168.0.60:80  check
    server    web1  192.168.0.61:80  check
  • haproxyの起動と自動起動設定(haproxy-01, haproxy-02)
# systemctl enable haproxy
# systemctl start haproxy
# vi /etc/keepalived/keepalived.conf
vrrp_script chk_haproxy {
    script "systemctl is-active haproxy"
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 1
    priority 101
    virtual_ipaddress {
        192.168.0.64
    }
    track_script {
        chk_haproxy
    }
}
# vi /etc/keepalived/keepalived.conf
vrrp_script chk_haproxy {
    script "systemctl is-active haproxy"
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 1
    priority 100
    virtual_ipaddress {
        192.168.0.64
    }
    track_script {
        chk_haproxy
    }
}
# systemctl enable keepalived
# systemctl start keepalived

動作確認

構築手順で実施した作業で、haproxy-01にVIP: 192.168.0.64が割り当てられているはずです。

# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:1a:4a:16:02:c7 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.62/25 brd 192.168.0.127 scope global eth0
       valid_lft forever preferred_lft forever
    inet 192.168.0.64/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::21a:4aff:fe16:2c7/64 scope link
       valid_lft forever preferred_lft forever

curlで確認しても、VIP: 192.168.0.64に対するリクエストがラウンドロビンでロードバランスされていることが分かるはずです。

# watch -n 0.1 curl 192.168.0.64

この状態で障害を想定し、haproxy-01のサービスhaproxyを停止します。

# systemctl stop haproxy

すると少ししてhaproxy-01からVIPが外れます。

# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:1a:4a:16:02:c7 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.62/25 brd 192.168.0.127 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::21a:4aff:fe16:2c7/64 scope link
       valid_lft forever preferred_lft forever

その後、haproxy-02にVIPが付与されます。(IPフェイルオーバ)

# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:1a:4a:16:02:c8 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.63/25 brd 192.168.0.127 scope global eth0
       valid_lft forever preferred_lft forever
    inet 192.168.0.64/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::21a:4aff:fe16:2c8/64 scope link
       valid_lft forever preferred_lft forever

VIP: 192.168.0.64に対するリクエストは引き続き処理できるようになっています。

# watch -n 0.1 curl 192.168.0.64

IPフェイルバックを試してみましょう。haproxy-01のサービスhaproxyを起動します。

# systemctl start haproxy

すると少ししてhaproxy-02からVIPが外れます。

# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:1a:4a:16:02:c8 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.63/25 brd 192.168.0.127 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::21a:4aff:fe16:2c8/64 scope link
       valid_lft forever preferred_lft forever

その後、haproxy-01にVIPが付与されます。(IPフェイルバック)

# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:1a:4a:16:02:c7 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.62/25 brd 192.168.0.127 scope global eth0
       valid_lft forever preferred_lft forever
    inet 192.168.0.64/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::21a:4aff:fe16:2c7/64 scope link
       valid_lft forever preferred_lft forever

今更ながらECSを試してみた

Dockerコンテナはこれまで自前でコンテナホストを建てて実行してましたが、パブリッククラウドで使うとどんな使用感なんだろう?と気になり、今更ですがAmazon ECSを使ってみました。今回は簡単にタスクを1個定義して実行してみただけですが、メモとして記事を残しておきます。

◆まずはコンテナ実行環境(クラスタ)を作ります。 f:id:bbrfkr:20180312221801p:plain

◆今回はLinuxでDockerホストを作りますー。 f:id:bbrfkr:20180312221851p:plain

クラスタの名前とインスタンスの情報とインスタンスを置くVPCの情報を記載して「作成」をクリック。 f:id:bbrfkr:20180312221920p:plain f:id:bbrfkr:20180312222041p:plain f:id:bbrfkr:20180312222121p:plain

◆しばらくするとCloudFormationでコンテナ実行環境ができます。 f:id:bbrfkr:20180312222304p:plain

◆コンテナ実行環境ができたら、次は実行するアプリケーション(タスク)を定義します。その中で実際に実行するコンテナを定義していきます。 f:id:bbrfkr:20180312222351p:plain f:id:bbrfkr:20180312222540p:plain f:id:bbrfkr:20180312222554p:plain

◆今回は「irohaboard」というLMSをデプロイします。まずはDBコンテナの定義ですね。環境変数とかも定義できますー。 f:id:bbrfkr:20180312222507p:plain f:id:bbrfkr:20180312222526p:plain

◆次に本体のirohaboardコンテナの定義です。Dockerのリンク機能で、MariaDBコンテナに接続します。 f:id:bbrfkr:20180312222715p:plain f:id:bbrfkr:20180312222725p:plain

◆実行コンテナの定義もできたら、「作成」をクリック。 f:id:bbrfkr:20180312222751p:plain

クラスタの画面に戻って、「タスク -> 新しいタスクの実行」をクリック。 f:id:bbrfkr:20180312222850p:plain f:id:bbrfkr:20180312222902p:plain

◆「タスクの実行」をクリックすると、コンテナの準備が始まります。 f:id:bbrfkr:20180312222946p:plain

◆しばらくすると、タスクの状態が「RUNNING」に、コンテナの状態も「RUNNING」になります。 f:id:bbrfkr:20180312223025p:plain f:id:bbrfkr:20180312223126p:plain

◆irohaboardコンテナの「外部リンク」のURLをクリックすると、出来上がったアプリケーションにアクセスできます。 f:id:bbrfkr:20180313210517p:plain f:id:bbrfkr:20180312223221p:plain

rktをインストールしてコンテナを起動してみた

巷でコンテナランタイムといえばまだまだDockerが有名ですが、他にもrkt、containerd、LXD、cri-oなど、いろいろあります。今回はrktをDebian 9にインストールしてコンテナを上げてみたのでその手順をメモメモです。

  • sidを登録
$ sudo vi /etc/apt/sources.list
...
deb http://ftp.jp.debian.org/debian/ sid main non-free contrib
deb-src http://ftp.jp.debian.org/debian/ sid main non-free contrib
  • rktのインストール
$ sudo apt-get update
$ sudo apt-get install rkt
  • Docker Hubのnginxイメージ取得
$ sudo rkt fetch --insecure-options=image docker://nginx:latest
  • nginxコンテナの起動(host port 8080番に暴露)
$ sudo rkt run --port=80-tcp:8080 registry-1.docker.io/library/nginx:latest

この状態でホストの8080番ポートに対してcurlするとnginxのおなじみのデフォルトページが返ってくるはずです。

$ curl localhost:8080

oVirt用のクラウドイメージテンプレートの作り方

またまたoVirtネタです。oVirtではcloud-initを使ってテンプレートからVMをプロビジョニングし、ネットワークなどの初期設定を行うことができます。これを実現するにはプロビジョニング元のテンプレートにcloud-initをインストールする必要があるのですが、単純にインストールするだけですとうまく初期設定ができずにはまったので、メモとして残しておきます。今回作ったのはCentOS7.4、Ubuntu16.04、Debian9のイメージです。

CentOS7.4イメージの作り方

  • Minimal installでOSをインストール
  • cloud-initをインストール。ですが、OS標準のYumリポジトリからインストールするのではなく、それよりも新しいバージョンのパッケージをcurlで引っ張ってきてインストールする。

参考URL: https://bugzilla.redhat.com/show_bug.cgi?id=1492726

# curl -O https://people.redhat.com/rmccabe/cloud-init/cloud-init-0.7.9-20.el7.x86_64.rpm
# yum -y install cloud-init-0.7.9-20.el7.x86_64.rpm
  • 必要に応じて、ovirt-guest-agentをインストール

手順URL: https://www.ovirt.org/documentation/how-to/guest-agent/install-the-guest-agent-in-centos/

Ubuntu16.04、Debian9のインストール

  • Minimal installでOSをインストール
  • cloud-initをインストール。その後、/etc/network/interfacesを以下のように書き換える。
$ sudo apt-get install cloud-init
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# Source interfaces
# Please check /etc/network/interfaces.d before changing this file
# as interfaces may have been defined in /etc/network/interfaces.d
# See LP: #1262951
source /etc/network/interfaces.d/*
  • 必要に応じて、ovirt-guest-agentをインストール。ただしインストール後、正常にサービスを起動するには以下の手順が必要。

手順URL(Ubuntu): https://www.ovirt.org/documentation/how-to/guest-agent/install-the-guest-agent-in-ubuntu/

手順URL(Debian): https://www.ovirt.org/documentation/how-to/guest-agent/install-the-guest-agent-in-debian/

$ sudo vi /etc/ovirt-guest-agent.conf
...
[virtio]
# device = /dev/virtio-ports/com.redhat.rhevm.vdsm
device = /dev/virtio-ports/ovirt-guest-agent.0     # この行を追加
...
$ sudo vi /etc/udev/rules.d/55-ovirt-guest-agent.rules # 新規作成
SYMLINK=="virtio-ports/ovirt-guest-agent.0", OWNER="ovirtagent", GROUP="ovirtagent"

oVirt環境のVMを管理するplaybookをさらす

久しぶりの投稿ですが、生きてますw 投稿しよう!と思いきれる真新しい発見なく過ごしていたので、放置気味でした…(汗

けど、細々としたことでもこまめにナレッジとして残しておいたほうが後々得すると思うので、少し更新頻度を上げたいと思います。 ということで今日はこんなAnsibleのplaybookを書いてみました、という話でも。

自宅ではoVirtという仮想化基盤でサーバ運用や技術検証を行っています。無償でライブマイグレーションの機能が使えたり、GlusterFSとのインテグレーションによってハイパーコンバージドインフラを構築できたりなど、個人利用には至れり尽くせりの機能を持っています。低予算かつ本格的な仮想化基盤を探している方は是非チェックしてみてください。

ところでこのoVirt、VMwareのようにAPIを叩くことも出来るようになっていて、API経由で仮想リソースを管理できるようになっています。つまり疑似プライベートクラウドのように使うことも可能なのです。そろそろお家環境もInfrastructure as Codeを適用しようと考えていたので、このAPIをバリバリ活用して自動化しようと思い立ちました。

それでインフラ構成をコード化するなら、最近ホットであり、仕事でも使っているAnsibleを使ってやろうということでplaybookを書いてみました。oVirtモジュールもVer 2.3で大量に導入されましたしね。数少ないoVirtユーザの参考になれば幸いです。

初めからconnection: localとせず、delegate_to: localhostを利用しているところがミソで、こうすることでVMを並列に作成・削除することができるようになります。また、host_varsのファイルもlocalhost.yamlに全VMのパラメータを格納することなく、<inventory_hostname>.yamlに各VMのパラメータを格納することができるようになり、保守性が高くなります!

◆manage-vm.yaml

---
- name: manage vm
  hosts: all
  gather_facts: no
  vars_files:
  - manage-vm_vars.yaml
  tasks:
  - name: login to ovirt
    ovirt_auth:
      url: https://<oVirt EngineのFQDN>/ovirt-engine/api
      username: admin@internal
      insecure: yes
      password: "{{ ovirt_admin_pass }}"
    connection: local
    delegate_to: localhost

  - block:
    - name: create disks
      ovirt_disk:
        auth: "{{ ovirt_auth }}"
        name: "{{ item.name }}"
        size: "{{ item.size }}"
        format: "{{ item.format }}"
        interface: "{{ item.interface }}"
        storage_domain: "{{ item.storage_domain }}"
        state: present
      with_items: "{{ ovirt_vm.persistent_disks | default([]) }}"

    - name: create vm
      ovirt_vms:
        auth: "{{ ovirt_auth }}"
        name: "{{ ovirt_vm.name }}"
        cluster: "{{ ovirt_cluster }}"
        template: "{{ ovirt_vm.template }}"
        memory: "{{ ovirt_vm.memory }}"
        cpu_cores: "{{ ovirt_vm.cpu_cores }}"
        nics: "{{ ovirt_vm.nics }}"
        cloud_init:
          host_name: "{{ ovirt_vm.hostname }}"
          dns_servers: "{{ ovirt_vm.dns_servers }}"
          user_name: "{{ ansible_user }}"
          root_password: "{{ ansible_ssh_pass | default('') }}"
          authorized_ssh_keys: "{{ ovirt_vm.authorized_keys | default('') }}"
          custom_script: "{{ ovirt_vm.custom_script | default('') }}"
        cloud_init_nics: "{{ ovirt_vm.nics }}"
        state: running

    - name: attach disks
      ovirt_disk:
        auth: "{{ ovirt_auth }}"
        name: "{{ item.name }}"
        vm_name: "{{ ovirt_vm.name }}"
        interface: "{{ item.interface }}"
        state: attached
      with_items: "{{ ovirt_vm.persistent_disks | default([]) }}"

    - name: wait for vm to be up
      wait_for:
        host: "{{ ansible_host }}"
        port: "{{ ansible_port }}"
        state: started
    when: ovirt_vm.state == "running"
    connection: local
    delegate_to: localhost

  - block:
    - name: detach disks
      ovirt_disk:
        auth: "{{ ovirt_auth }}"
        name: "{{ item.name }}"
        vm_name: "{{ ovirt_vm.name }}"
        state: detached
      when: item.protect_from_deletion | default(true)
      with_items: "{{ ovirt_vm.persistent_disks | default([]) }}"

    - name: remove vm
      ovirt_vms:
        auth: "{{ ovirt_auth }}"
        name: "{{ ovirt_vm.name }}"
        cluster: "{{ ovirt_cluster }}"
        state: absent
    when: ovirt_vm.state == "absent"
    connection: local
    delegate_to: localhost

◆manage-vm_vars.yaml

---
ovirt_admin_pass: <adminユーザのパスワード>
ovirt_cluster: <対象クラスタ名>

◆host_vars/<inventory_hostname>.yaml (サンプル)

---
ansible_host: <対象サーバのIP>
ansible_user: root
ansible_ssh_pass: <rootユーザのパスワード>
ansible_port: 22
ovirt_vm:
  name: <対象サーバの名前>
  template: <利用するテンプレート名(要cloud-init導入)>
  memory: 4GiB
  cpu_cores: 2
  hostname: <対象サーバのFQDN>
  dns_servers: <参照するDNSサーバ(カンマ区切り)>
  nics:
  - nic_boot_protocol: static
    nic_ip_address: "{{ ansible_host }}"
    nic_netmask: <サブネットマスク>
    nic_gateway: <デフォルトゲートウェイ>
    nic_name: eth0
    nic_on_boot: yes
    name: nic1
    interface: virtio
    profile_name : ovirtmgmt
  persistent_disks:
  - name: <追加するディスクの名前>
    size: 30GiB
    format: cow
    interface: virtio
    storage_domain: <ディスクの所属するストレージドメイン>
    protect_from_deletion: <削除時にこのディスクを保護するか(Boolean)>
  state: running