ビビリフクロウの足跡

とあるインフラSEの勉強&備忘ブログ

Kubernetes the Hard Way on OpenStack

Kubernetesの構築をスクラッチで実践できるチュートリアルとしてKubernetes the Hard Wayがあります。来年のはじめくらいにCKAを取得したいと考えているので、本チュートリアルを自宅のOpenStack上で実施してみました。その際の実施手順と少しばかりの解説を残しておきたいと思います。

前提

OpenStackには次のコンポーネントがインストールされているとします。

  • Keystone
  • Glance
  • Nova
  • Neutron
  • Neutron LBaaSv2
  • Designate

次のインスタンスを用意し、HA構成のKubernetesを構成します。 ホスト名でFloatingIPに名前解決ができるようにDesignateを構成しておきます。

  • Masterサーバ x3

    • OS: CentOS 7 Minimal Install
    • vCPU: 2
    • Mem: 4GB
    • ホスト名: hardway-master-[0:2].bbrfkr.mydns.jp
    • IP: 10.0.0.[10:12]
    • FloatingIP: 192.168.0.[10:12]
  • Workerサーバ x3

    • OS: CentOS 7 Minimal Install
    • vCPU: 2
    • Mem: 4GB
    • ホスト名: hardway-worker-[0:2].bbrfkr.mydns.jp
    • IP: 10.0.0.[20:22]
    • FloatingIP: 192.168.0.[20:22]

また、FloatingIPに対して到達可能なネットワーク上に作業用端末を用意しておきます。作業用端末ではあらかじめ以下のコマンドが叩けるように準備しておきます。私の場合はWindows 10にWSLのUbuntuをインストールして、その上から叩けるようにしました。

  • cfssl
  • cfssljson
  • kubectl
  • openstack
  • neutron

Masterサーバのkube-apiserverに対するエンドポイントとしてNeutron LBaaSv2にてロードバランサを切っておきます。また、VIP portに対するセキュリティグループも適切に設定しておきましょう。

  • Neutron LBaaSv2 LB
    • 名前: kubernetes-the-hard-way
    • Internal VIP: 10.0.0.100
    • FloatingIP: 192.168.0.100
    • リスナーポート: 6443

手順

以降、Hard Wayな手順を示して行きます。作業対象は()付きでmasters、workers、bastion(作業用端末の意)で示していきます。

OS設定

  • (masters, workers) OSパッケージの更新とSELinuxの無効化をしておきます。これはKubernetesのインストール要件になります。
sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config
sudo yum update -y
sudo reboot

CAおよび各種証明書の作成

ここではkube-apiserverとhttps通信するために必要な各種証明書を作成していきます。

  • (bastion) CAの署名における設定とCA証明書および鍵を作成します。
cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "kubernetes": {
        "usages": ["signing", "key encipherment", "server auth", "client auth"],
        "expiry": "8760h"
      }
    }
  }
}
EOF

cat > ca-csr.json <<EOF
{
  "CN": "Kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "CA",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert -initca ca-csr.json | cfssljson -bare ca
  • (bastion) Kubernetesクラスタのadminユーザに対するクライアント証明書および鍵を作成します。Kubernetesのx509証明書認証においてはCN(Common Name)に指定した値がクラスタのユーザ名、O(Organization)に指定した値がグループ名として扱われます。system:mastersグループはデフォルトでKubernetesクラスタのフルコントロール権限を持つグループです。
cat > admin-csr.json <<EOF
{
  "CN": "admin",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:masters",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  admin-csr.json | cfssljson -bare admin
  • (bastion) kubeletがkube-apiserverと通信するときのクライアント証明書および鍵を作成します。ここでは厳密にはNode Authorizationというkube-apiserverの認可機能を使うために、CNにsystem:node:<インスタンス名>、Oにsystem:nodesという値を指定するのが肝となります。
for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; do
cat > ${instance}-csr.json <<EOF
{
  "CN": "system:node:${instance}",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:nodes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

EXTERNAL_IP=$(openstack server show -f json ${instance} | \
              jq -r .addresses | \
              awk -F= '{ print $2 }' | \
              sed 's/,//g' | \
              awk '{ print $2 }')

INTERNAL_IP=$(openstack server show -f json ${instance} | \
              jq -r .addresses | \
              awk -F= '{ print $2 }' | \
              sed 's/,//g' | \
              awk '{ print $1 }')

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=${instance},${EXTERNAL_IP},${INTERNAL_IP} \
  -profile=kubernetes \
  ${instance}-csr.json | cfssljson -bare ${instance}
done
  • (bastion) kube-controller-managerがkube-apiserverと通信するための証明書および鍵を作成します。
cat > kube-controller-manager-csr.json <<EOF
{
  "CN": "system:kube-controller-manager",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:kube-controller-manager",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager
  • (bastion) kube-proxyがkube-apiserverと通信するための証明書および鍵を作成します。
cat > kube-proxy-csr.json <<EOF
{
  "CN": "system:kube-proxy",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:node-proxier",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-proxy-csr.json | cfssljson -bare kube-proxy
  • (bastion) kube-schedulerがkube-apiserverと通信するための証明書および鍵を作成します。
cat > kube-scheduler-csr.json <<EOF
{
  "CN": "system:kube-scheduler",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:kube-scheduler",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-scheduler-csr.json | cfssljson -bare kube-scheduler
  • (bastion) kube-apiserver用のサーバ証明書および鍵を作成します。この証明書で許可されたCNやSAN(Subject Alternative Name)でアクセスしなければkube-apiservertとhttps通信できないので、登録するSAN(cfssl gencertコマンドの-hostnameオプションのところ)に注意します。
KUBERNETES_PUBLIC_ADDRESS=$(neutron floatingip-list | grep \
                          $(neutron lbaas-loadbalancer-show kubernetes-the-hard-way | \
                            grep vip_port_id | \
                            awk '{ print $4 }') | \
                          awk '{ print $8 }')
KUBERNETES_INTERNAL_ADDRESS=$(neutron lbaas-loadbalancer-show kubernetes-the-hard-way | \
                              grep vip_address | \
                              awk '{ print $4 }')
KUBERNETES_PUBLIC_DNS=kubernetes-the-hard-way.bbrfkr.mydns.jp

cat > kubernetes-csr.json <<EOF
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
MASTER_INTERNAL_IPS=${MASTER_INTERNAL_IPS}$(openstack server show -f json ${instance} | \
              jq -r .addresses | \
              awk -F= '{ print $2 }' | \
              sed 's/,//g' | \
              awk '{ print $1 }'),
done

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=10.32.0.1,${MASTER_INTERNAL_IPS}${KUBERNETES_PUBLIC_ADDRESS},${KUBERNETES_INTERNAL_ADDRESS},${KUBERNETES_PUBLIC_DNS},127.0.0.1,kubernetes.default \
  -profile=kubernetes \
  kubernetes-csr.json | cfssljson -bare kubernetes
  • (bastion) ServiceAccountのトークン発行用キーペアを作成します。
cat > service-account-csr.json <<EOF
{
  "CN": "service-accounts",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  service-account-csr.json | cfssljson -bare service-account
  • (bastion) 証明書群をMasterサーバ、Workerサーバに配布します。
for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; do
  scp ca.pem ${instance}-key.pem ${instance}.pem centos@${instance}.bbrfkr.mydns.jp:~/
done

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
  scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
    service-account-key.pem service-account.pem centos@${instance}.bbrfkr.mydns.jp:~/
done

kubeconfig群の作成

ここではkube-apiserverと通信するために必要な認証ファイルであるkubeconfigファイル群を作成していきます。

  • (bastion) kubelet用のkubeconfigを作成します。
KUBERNETES_PUBLIC_ADDRESS=$(neutron floatingip-list | grep \
                          $(neutron lbaas-loadbalancer-show kubernetes-the-hard-way | \
                            grep vip_port_id | \
                            awk '{ print $4 }') | \
                          awk '{ print $8 }')

for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; do
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \
    --kubeconfig=${instance}.kubeconfig

  kubectl config set-credentials system:node:${instance} \
    --client-certificate=${instance}.pem \
    --client-key=${instance}-key.pem \
    --embed-certs=true \
    --kubeconfig=${instance}.kubeconfig

  kubectl config set-context default \
    --cluster=kubernetes-the-hard-way \
    --user=system:node:${instance} \
    --kubeconfig=${instance}.kubeconfig

  kubectl config use-context default --kubeconfig=${instance}.kubeconfig
done
  • (bastion) kube-proxy用のkubeconfigを作成します。
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \
  --kubeconfig=kube-proxy.kubeconfig

kubectl config set-credentials system:kube-proxy \
  --client-certificate=kube-proxy.pem \
  --client-key=kube-proxy-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-proxy.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-proxy \
  --kubeconfig=kube-proxy.kubeconfig

kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
  • (bastion) kube-controller-manager用のkubeconfigを作成します。
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://127.0.0.1:6443 \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config set-credentials system:kube-controller-manager \
  --client-certificate=kube-controller-manager.pem \
  --client-key=kube-controller-manager-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-controller-manager \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig
  • (bastion) kube-scheduler用のkubeconfigを作成します。
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://127.0.0.1:6443 \
  --kubeconfig=kube-scheduler.kubeconfig

kubectl config set-credentials system:kube-scheduler \
  --client-certificate=kube-scheduler.pem \
  --client-key=kube-scheduler-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-scheduler.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-scheduler \
  --kubeconfig=kube-scheduler.kubeconfig

kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig
  • (bastion) adminユーザ用のkubeconfigを作成します。
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://127.0.0.1:6443 \
  --kubeconfig=admin.kubeconfig

kubectl config set-credentials admin \
  --client-certificate=admin.pem \
  --client-key=admin-key.pem \
  --embed-certs=true \
  --kubeconfig=admin.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=admin \
  --kubeconfig=admin.kubeconfig

kubectl config use-context default --kubeconfig=admin.kubeconfig
  • (bastion) kubeconfig群をMasterサーバ、Workerサーバに配布します。
for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; do
  scp ${instance}.kubeconfig kube-proxy.kubeconfig centos@${instance}.bbrfkr.mydns.jp:~/
done

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
  scp admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig centos@${instance}.bbrfkr.mydns.jp:~/
done

データ暗号化用設定ファイルの作成

Kubernetesではetcdにクラスタ情報が格納されますが、ここではその格納データを暗号化するための設定を実施します。

  • (bastion) クラスタデータ暗号化用設定ファイルを作成し、Masterサーバに配布します。
ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
cat > encryption-config.yaml <<EOF
kind: EncryptionConfig
apiVersion: v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: ${ENCRYPTION_KEY}
      - identity: {}
EOF

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
  scp encryption-config.yaml centos@${instance}.bbrfkr.mydns.jp:~/
done

etcdクラスタの構築

ここではMasterサーバ上でetcdのHAクラスタを構築します。

  • (masters) etcdバイナリのダウンロードと適切な場所への配置を行います。
sudo yum -y install wget
wget "https://github.com/coreos/etcd/releases/download/v3.3.9/etcd-v3.3.9-linux-amd64.tar.gz"

tar -xvf etcd-v3.3.9-linux-amd64.tar.gz
sudo mv etcd-v3.3.9-linux-amd64/etcd* /usr/local/bin/
  • (masters) etcd用のsystemd unitファイルを作成し、サービスを有効化します。
sudo mkdir -p /etc/etcd /var/lib/etcd
sudo cp ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/

INTERNAL_IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
ETCD_NAME=$(hostname -s)
MASTER_0_HOSTNAME=hardway-master-0
MASTER_0_INTERNAL_IP=10.0.0.10
MASTER_1_HOSTNAME=hardway-master-1
MASTER_1_INTERNAL_IP=10.0.0.11
MASTER_2_HOSTNAME=hardway-master-2
MASTER_2_INTERNAL_IP=10.0.0.12
cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=etcd
Documentation=https://github.com/coreos

[Service]
ExecStart=/usr/local/bin/etcd \\
  --name ${ETCD_NAME} \\
  --cert-file=/etc/etcd/kubernetes.pem \\
  --key-file=/etc/etcd/kubernetes-key.pem \\
  --peer-cert-file=/etc/etcd/kubernetes.pem \\
  --peer-key-file=/etc/etcd/kubernetes-key.pem \\
  --trusted-ca-file=/etc/etcd/ca.pem \\
  --peer-trusted-ca-file=/etc/etcd/ca.pem \\
  --peer-client-cert-auth \\
  --client-cert-auth \\
  --initial-advertise-peer-urls https://${INTERNAL_IP}:2380 \\
  --listen-peer-urls https://${INTERNAL_IP}:2380 \\
  --listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \\
  --advertise-client-urls https://${INTERNAL_IP}:2379 \\
  --initial-cluster-token etcd-cluster-0 \\
  --initial-cluster ${MASTER_0_HOSTNAME}=https://${MASTER_0_INTERNAL_IP}:2380,${MASTER_1_HOSTNAME}=https://${MASTER_1_INTERNAL_IP}:2380,${MASTER_2_HOSTNAME}=https://${MASTER_2_INTERNAL_IP}:2380 \\
  --initial-cluster-state new \\
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable etcd
sudo systemctl start etcd
  • (masters) etcdの正常動作確認をするには以下のコマンドを実行します。
sudo su - -lc \
"ETCDCTL_API=3 etcdctl member list \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/etcd/ca.pem \
--cert=/etc/etcd/kubernetes.pem \
--key=/etc/etcd/kubernetes-key.pem"

Masterサーバの構築

ここではMasterサーバの構築を実施します。

sudo mkdir -p /etc/kubernetes/config
  • (masters) Masterサーバ用コンポーネントのバイナリをダウンロードし、適切な場所に配置します。
wget \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-apiserver" \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-controller-manager" \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-scheduler" \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kubectl"

chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl
sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/
  • (masters) kube-apiserver用の証明書群の配置とsystemd unitファイルを作成します。ここでKubernetesのServiceリソースで利用するIP帯10.32.0.0/24を指定します。
sudo mkdir -p /var/lib/kubernetes/
sudo mv ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
  service-account-key.pem service-account.pem \
  encryption-config.yaml /var/lib/kubernetes/

INTERNAL_IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
MASTER_0_INTERNAL_IP=10.0.0.10
MASTER_1_INTERNAL_IP=10.0.0.11
MASTER_2_INTERNAL_IP=10.0.0.12

cat <<EOF | sudo tee /etc/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
  --advertise-address=${INTERNAL_IP} \\
  --allow-privileged=true \\
  --apiserver-count=3 \\
  --audit-log-maxage=30 \\
  --audit-log-maxbackup=3 \\
  --audit-log-maxsize=100 \\
  --audit-log-path=/var/log/audit.log \\
  --authorization-mode=Node,RBAC \\
  --bind-address=0.0.0.0 \\
  --client-ca-file=/var/lib/kubernetes/ca.pem \\
  --enable-admission-plugins=Initializers,NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
  --enable-swagger-ui=true \\
  --etcd-cafile=/var/lib/kubernetes/ca.pem \\
  --etcd-certfile=/var/lib/kubernetes/kubernetes.pem \\
  --etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \\
  --etcd-servers=https://${MASTER_0_INTERNAL_IP}:2379,https://${MASTER_1_INTERNAL_IP}:2379,https://${MASTER_2_INTERNAL_IP}:2379 \\
  --event-ttl=1h \\
  --experimental-encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
  --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
  --kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \\
  --kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \\
  --kubelet-https=true \\
  --runtime-config=api/all \\
  --service-account-key-file=/var/lib/kubernetes/service-account.pem \\
  --service-cluster-ip-range=10.32.0.0/24 \\
  --service-node-port-range=30000-32767 \\
  --tls-cert-file=/var/lib/kubernetes/kubernetes.pem \\
  --tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (masters) kube-controller-manager用のkubeconfigの配置とsystemd unitファイルを作成します。--cluster-cidrオプションにはPodが利用するIP帯172.17.0.0/16を指定しておきます。
sudo mv kube-controller-manager.kubeconfig /var/lib/kubernetes/
cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-controller-manager \\
  --address=0.0.0.0 \\
  --cluster-cidr=172.17.0.0/16 \\
  --cluster-name=kubernetes \\
  --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \\
  --cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \\
  --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
  --leader-elect=true \\
  --root-ca-file=/var/lib/kubernetes/ca.pem \\
  --service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \\
  --service-cluster-ip-range=10.32.0.0/24 \\
  --use-service-account-credentials=true \\
  --allocate-node-cidrs=true \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (masters) kube-scheduler用の設定ファイルおよびkubeconfigの配置とsystemd unitファイルを作成します。
sudo mv kube-scheduler.kubeconfig /var/lib/kubernetes/
cat <<EOF | sudo tee /etc/kubernetes/config/kube-scheduler.yaml
apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
clientConnection:
  kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig"
leaderElection:
  leaderElect: true
EOF

cat <<EOF | sudo tee /etc/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-scheduler \\
  --config=/etc/kubernetes/config/kube-scheduler.yaml \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (masters) Masterサーバの各種サービスを有効化します。
sudo systemctl daemon-reload
sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler
sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler
  • (masters(hardway-master-0のみ)) kube-apiserverがWorkerサーバのkubeletに接続可能にするため、RBACの設定を入れておきます。
cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-apiserver-to-kubelet
rules:
  - apiGroups:
      - ""
    resources:
      - nodes/proxy
      - nodes/stats
      - nodes/log
      - nodes/spec
      - nodes/metrics
    verbs:
      - "*"
EOF

cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: system:kube-apiserver
  namespace: ""
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-apiserver-to-kubelet
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: kubernetes
EOF

Workerサーバの構築

ここではWorkerサーバを構築し、Kubernetesクラスタに参加させます。

  • (workers) OS依存パッケージのインストールを行います。
sudo yum -y install socat conntrack ipset
  • (workers) Workerサーバ用コンポーネントのバイナリをダウンロードし、適切な場所に配置します。
sudo yum -y install wget
wget \
  https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.13.0/crictl-v1.13.0-linux-amd64.tar.gz \
    https://github.com/containernetworking/plugins/releases/download/v0.6.0/cni-plugins-amd64-v0.6.0.tgz \
  https://github.com/opencontainers/runc/releases/download/v1.0.0-rc5/runc.amd64 \
  https://github.com/containerd/containerd/releases/download/v1.2.0-rc.0/containerd-1.2.0-rc.0.linux-amd64.tar.gz \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kubectl \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-proxy \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kubelet

sudo mkdir -p \
  /var/lib/kubelet \
  /var/lib/kube-proxy \
  /var/lib/kubernetes \
  /var/run/kubernetes \
  /etc/cni/net.d \
  /opt/cni/bin

sudo mv runc.amd64 runc
chmod +x kubectl kube-proxy kubelet runc
sudo mv kubectl kube-proxy kubelet runc /usr/local/bin/
sudo tar -xvf crictl-v1.13.0-linux-amd64.tar.gz -C /usr/local/bin/
sudo tar -xvf cni-plugins-amd64-v0.6.0.tgz -C /opt/cni/bin/
sudo tar -xvf containerd-1.2.0-rc.0.linux-amd64.tar.gz
sudo mv bin/* /bin
sudo rmdir bin
  • (workers) CNI(Container Network Interface)のloopbackインタフェースに対する設定を実施します。
cat <<EOF | sudo tee /etc/cni/net.d/99-loopback.conf
{
    "cniVersion": "0.3.1",
    "type": "loopback"
}
EOF
  • (workers) コンテナランタイム containerdの設定ファイルとsystemd unitファイルを配置します。本家Kubernetes the Hard WayではgVisorにも対応した設定を行っていますが、CentOS 7のカーネルがデフォルトでは古く、gVisorに対応していないため、省いています。
sudo mkdir -p /etc/containerd/
cat << EOF | sudo tee /etc/containerd/config.toml
[plugins]
  [plugins.cri.containerd]
    snapshotter = "overlayfs"
    [plugins.cri.containerd.default_runtime]
      runtime_type = "io.containerd.runtime.v1.linux"
      runtime_engine = "/usr/local/bin/runc"
      runtime_root = ""
EOF

cat <<EOF | sudo tee /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target

[Service]
ExecStartPre=/sbin/modprobe overlay
ExecStart=/bin/containerd
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity

[Install]
WantedBy=multi-user.target
EOF
  • (workers) kubelet用の証明書群およびkubeconfigを配置し、設定ファイルとsystemd unitファイルを作成します。
WORKER_HOSTNAME=$(hostname -s)
sudo mv ${WORKER_HOSTNAME}-key.pem ${WORKER_HOSTNAME}.pem /var/lib/kubelet/
sudo mv ${WORKER_HOSTNAME}.kubeconfig /var/lib/kubelet/kubeconfig
sudo mv ca.pem /var/lib/kubernetes/

cat <<EOF | sudo tee /var/lib/kubelet/kubelet-config.yaml
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
  x509:
    clientCAFile: "/var/lib/kubernetes/ca.pem"
authorization:
  mode: Webhook
clusterDomain: "cluster.local"
clusterDNS:
  - "10.32.0.10"
resolvConf: "/etc/resolv.conf"
runtimeRequestTimeout: "15m"
tlsCertFile: "/var/lib/kubelet/${WORKER_HOSTNAME}.pem"
tlsPrivateKeyFile: "/var/lib/kubelet/${WORKER_HOSTNAME}-key.pem"
EOF

cat <<EOF | sudo tee /etc/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=containerd.service
Requires=containerd.service

[Service]
ExecStart=/usr/local/bin/kubelet \\
  --config=/var/lib/kubelet/kubelet-config.yaml \\
  --container-runtime=remote \\
  --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \\
  --image-pull-progress-deadline=2m \\
  --kubeconfig=/var/lib/kubelet/kubeconfig \\
  --network-plugin=cni \\
  --register-node=true \\
  --hostname-override=${WORKER_HOSTNAME}
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (workers) kube-proxy用のkubeconfigを配置し、設定ファイルとsystemd unitファイルを作成します。
sudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig

cat <<EOF | sudo tee /var/lib/kube-proxy/kube-proxy-config.yaml
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
clientConnection:
  kubeconfig: "/var/lib/kube-proxy/kubeconfig"
mode: "iptables"
clusterCIDR: "172.17.0.0/16"
EOF

cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube Proxy
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-proxy \\
  --config=/var/lib/kube-proxy/kube-proxy-config.yaml
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (workers) Workerサーバのサービスを有効化します。同時にKubernetesクラスタへWorkerサーバ登録が行われます。
sudo systemctl daemon-reload
sudo systemctl enable containerd kubelet kube-proxy
sudo systemctl start containerd kubelet kube-proxy

CNI flannelとサービスディスカバリ corednsの導入

ここでは、作業用端末のkubectlを作成したKubernetesクラスタに接続できるよう設定し、CNIとサービスディスカバリを導入して最低限、Kubernetesが機能するようにします。

kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://kubernetes-the-hard-way.bbrfkr.mydns.jp:6443

kubectl config set-credentials admin \
  --client-certificate=admin.pem \
  --client-key=admin-key.pem

kubectl config set-context kubernetes-the-hard-way \
  --cluster=kubernetes-the-hard-way \
  --user=admin

kubectl config use-context kubernetes-the-hard-way
  • (bastion) CNI flannelをデプロイします。
wget https://raw.githubusercontent.com/coreos/flannel/bc79dd1505b0c8681ece4de4c0d86c5cd2643275/Documentation/kube-flannel.yml
sed -i 's@10.244.0.0/16@172.17.0.0/16@g' kube-flannel.yml
kubectl apply -f kube-flannel.yml
  • (bastion) サービスディスカバリ corednsをデプロイします。
kubectl apply -f https://storage.googleapis.com/kubernetes-the-hard-way/coredns.yaml

virtual-kubeletをAWS Fargateで試してみた

現在開催中のJapan Container Days 1812の基調講演でvirtual-kubeletなるプロダクトを聞いたので、サクッと試してみました。

virtual-kubeletとは

Kubernetesにおけるkubeletの実装の一つで、podの作成先を他のプラットフォームのAPIに依頼することができるとのこと。これによって、AzureのACIやAWSのFargateなどのサーバレスフレームワークKubernetesと連携、Kubernetes側で操作できるようになります。

今回の前提条件

以下の環境を前提に解説します。

Get Started!

まず、virtual-kubeletを動作させるVMにGoとawscliを導入します。この導入手順は割愛します。

次にvirtual-kubeletのバイナリをビルドします。

mkdir -p $GOPATH/src/github.com/virtual-kubelet
cd $GOPATH/src/github.com/virtual-kubelet
git clone https://github.com/virtual-kubelet/virtual-kubelet
cd virtual-kubelet
make build

ビルドしたら、バイナリをパスの通るところに置きます。

mv bin/virtual-kubelet /usr/bin/

続いてvirtual-kubeletのprovider設定ファイルを作ります。

mkdir -p /etc/kubernetes/
cp providers/aws/fargate.toml /etc/kubernetes/
cat <<EOF > /etc/kubernetes/fargate.toml
Region = "<Fargateクラスタのあるリージョン>"
ClusterName = "<Fargateクラスタの名前>"
Subnets = [<Fargateクラスタの所属するサブネットのカンマ区切りのリスト>]
SecurityGroups = ["<タスクにアタッチしたいSG>"]
AssignPublicIPv4Address = true
ExecutionRoleArn = ""
CloudWatchLogGroupName = ""
PlatformVersion = "LATEST"
OperatingSystem = "Linux"
CPU = "20"
Memory = "40Gi"
Pods = "20"
EOF

virtual-kubeletがKubernetesのマスターに接続するためのkubeconfigをここで作成します。

cat <<EOF > /etc/kubernetes/virtual-kubelet.conf
<kubeconfigの中身>
EOF

最後にsystemdのunitファイルと環境変数ファイルを作ります。

cat <<EOF > /etc/sysconfig/virtual-kubelet
# provider name (required)
PROVIDER_NAME="aws"
# provider config file path (required)
PROVIDER_CONFIG="/etc/kubernetes/fargate.toml"
# virtual-kubelet port (required)
KUBELET_PORT=10250
# other options
OPTIONS="--kubeconfig=/etc/kubernetes/virtual-kubelet.conf"
EOF
cat <<EOF > /usr/lib/systemd/system/virtual-kubelet.service
[Unit]
Description=Virtual Kubelet

[Service]
User=root
EnvironmentFile=-/etc/sysconfig/virtual-kubelet
ExecStart=/usr/bin/virtual-kubelet --provider \$PROVIDER_NAME --provider-config \$PROVIDER_CONFIG \$OPTIONS

[Install]
WantedBy=multi-user.target
EOF

最後にvirtual-kubeletサービスを有効化します。

systemctl daemon-reload
systemctl enable virtual-kubelet
systemctl start virtual-kubelet

以上で、Kubernetesにvirtual-kubeletというノード名のノードが追加されているはずです。

kubectl get node
NAME                             STATUS                     ROLES    AGE   VERSION
test-k8s-zpo7woffkomq-master-0   Ready,SchedulingDisabled   <none>   11h   v1.11.1
test-k8s-zpo7woffkomq-minion-0   Ready                      <none>   11h   v1.11.1
virtual-kubelet                  Ready                      agent    9h    v1.11.2

試しにvirtual-kubeletでPodが起動するようなDeploymentを作ってみます。

cat <<EOF | kubectl apply -f -
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: httpd
  name: httpd
  namespace: default
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: httpd
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: httpd
    spec:
      containers:
      - image: httpd
        imagePullPolicy: Always
        name: httpd
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      nodeSelector:
        kubernetes.io/role: agent
        beta.kubernetes.io/os: linux
        type: virtual-kubelet
      tolerations:
      - key: virtual-kubelet.io/provider
        operator: Exists
      - key: azure.com/aci
        effect: NoSchedule
EOF

しばらくするとPodがRunningになります。

kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
httpd-7b5896b895-45zh6   1/1     Running   0          44s

Fargate側を確認すると、タスクが起動していることがわかります!

f:id:bbrfkr:20181205103801p:plain

Neutron LBaaSのオートヒーリング設定

少しずつ、プライベートクラウド(OpenStack)の構築時ナレッジを書いていこうと思います。本日はNeutron LBaaSについてです。

Neutron LBaaS自身、Octaviaにその役割を譲りつつあり、Deprecateなのですが、LB用VM立てるよりもHAproxyのプロセスをネットワークノードに立てるほうがデプロイが高速なので、いまだに使っています。可用性を気にしなくていい環境では手軽にLBをデプロイできるので、なくなってほしくないですね。。。

Neutron LBaaSですが、デフォルトではLB(HAproxy)が起動しているネットワークノードが死ぬと、当然ながらLBも死んでしまいます。しかし、ネットワークノードが冗長化されている状況では、別ノードでLBを再起動してほしいわけです。そんなことを実現してくれるneutron.confのパラメータが用意されています。

allow_automatic_lbaas_agent_failover = true

このパラメータを設定し、LBaaS Agentを冗長化するだけで、LBのオートヒーリングが実現できます!

docker-composeでNginx + Django + MySQLのWeb三階層を構成する

Kubernetesクラスタの構築方法など、インフラ観点での記事を書くことが多い本ブログですが、Kubernetesを学んでみると

  • インフラエンジニアもアプリケーションエンジニアの気持ちがわかったほうがいい!

というお気持ちになるのです。CI/CDとか考えると特に。

そんなことでOSにも標準でインストールされている意味でメジャーな言語であるPythonを、そのWebフレームワークであるDjangoを通して勉強しているわけなのですが、DjangoSQLiteとか、python manage.py runserverとか使っていると、「本番環境だとこれどう変わるのん?」というインフラエンジニアならではの疑問が沸々と湧き上がってくるのです。

そこで、Pythonのお勉強のわき道にそれて、Nginx + Django + MySQLでWeb三階層を組んでみました。せっかくDockerコンテナのことも知っているので、docker-composeを使い、コンテナでWeb三階層を組んでみました。そのときのdocker-compose.ymlをさらしたいと思います。

いきなりdocker-compose.ymlをさらす前に、プロジェクトのディレクトリ構成を示しておきます。

.
├── docker-compose.yml        
├── mysite                   ← Djangoプロジェクトのルート
│   ├── Dockerfile             チュートリアルアプリであるpollsアプリを作成
│   ├── docker-entrypoint.sh
│   └── mysite
│       ├── manage.py
│       ├── mysite
│       ├── polls
│       └── static
├── mysql                    ← MySQLプロジェクトのルート
│   └── Dockerfile             mysql:5.7イメージを使うだけ
└── nginx                    ← Nginxプロジェクトのルート
    ├── Dockerfile             基本的にはnginx:1.15.alpineを使うだけだが、
    ├── docker-entrypoint.sh   Djangoと連携するためのUWSGI接続設定を組み込む
    └── mysite_nginx.conf

この前提の下で、docker-compose.ymlは以下のように書きました。

version: '3'

services:
  mysite:
    build: mysite
    image: bbrfkr0129/mysite
    environment:
      MYSQL_DATABASE: mysite
      MYSQL_USER: mysite
      MYSQL_PASSWORD: password
      MYSQL_HOSTNAME: mysql
    networks:
    - mysite
  mysql:
    build: mysql
    image: bbrfkr0129/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: mysite
      MYSQL_USER: mysite
      MYSQL_PASSWORD: password
    volumes:
    - "mysql_data:/var/lib/mysql"
    networks:
    - mysite
  nginx:
    build: nginx
    image: bbrfkr0129/nginx
    ports:
    - "80:80"
    environment:
      UWSGI_HOST: mysite
    volumes:
    - "./mysite/mysite/static:/static"
    networks:
    - mysite

volumes:
  mysql_data:
    driver: local

networks:
  mysite:
    driver: bridge

mysqlサービスは、公式の使い方に準拠しているので、説明を省きます。 mysiteサービスではDjangoをuWSGI接続で動作させます。バックエンドDBにMySQLを使うので、あらかじめsettings.pyMySQL接続用のパラメータを置換用文字列で書き換えておき、コンテナ起動時にdocker-entrypoint.shの処理内で実際のパラメータに置換します。Dockerfiledocker-entrypoint.shは以下のような感じです。

FROM python:3.6-alpine3.8

# install django uwsgi and PyMySQL
RUN apk --no-cache --virtual .requirements add g++ make linux-headers && \
    apk --no-cache add libffi-dev mysql-dev mysql-client && \
    pip install Django uwsgi mysqlclient && rm -rf ~/.cache/pip && \
    apk del .requirements

# install mysite
RUN mkdir /mysite
COPY ./mysite/ /mysite/
WORKDIR /mysite

EXPOSE 8001
COPY docker-entrypoint.sh /usr/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["uwsgi", "--socket", ":8001", "--module", "mysite.wsgi"]
#!/bin/sh
sed -i "s/MYSQL_DATABASE/$MYSQL_DATABASE/g" /mysite/mysite/settings.py
sed -i "s/MYSQL_USER/$MYSQL_USER/g" /mysite/mysite/settings.py
sed -i "s/MYSQL_PASSWORD/$MYSQL_PASSWORD/g" /mysite/mysite/settings.py
sed -i "s/MYSQL_HOSTNAME/$MYSQL_HOSTNAME/g" /mysite/mysite/settings.py

export connected="no"
while [ $connected = "no" ]
do
  mysql \
    -u$MYSQL_USER -p$MYSQL_PASSWORD -h$MYSQL_HOSTNAME \
    -e "show tables;" $MYSQL_DATABASE
  if [ $? -eq 0 ] ; then
    export connected="yes"
  fi 
  sleep 1
done 

python manage.py makemigrations polls
python manage.py migrate 
python manage.py shell -c "\
from django.contrib.auth import get_user_model;\
User = get_user_model();\
User.objects.create_superuser('admin', 'admin@example.com', 'admin')\
"

exec "$@"

nginxサービスでは基本的にnginx:1.15-alpineイメージを使いますが、DjangoのuWSGIに接続するための情報が必要なので、このための設定ファイルをdocker buildで埋め込み、コンテナ起動時にdocker-entrypoint.shの処理内でパラメータに置換します。Dockerfilemysite_nginx.confdocker-entrypoint.shは以下のような感じです。

# the upstream component nginx needs to connect to
upstream django {
    # server unix:///mysite/mysite.sock; # for a file socket
    server UWSGI_HOST:8001; # for a web port socket (we'll use this first)
}

# configuration of the server
server {
    # the port your site will be served on
    listen      80;
    # the domain name it will serve for
    server_name example.com; # substitute your machine's IP address or FQDN
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    location /static {    
        alias /static; 
    }

    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  django;
        include     /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
    }
}
FROM nginx:1.15-alpine

RUN rm -f /etc/nginx/conf.d/*
COPY mysite_nginx.conf /etc/nginx/conf.d/mysite_nginx.conf
COPY docker-entrypoint.sh /usr/bin/
RUN chmod 755 /usr/bin/docker-entrypoint.sh
RUN mkdir /static
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
#!/bin/sh
sed -i "s/UWSGI_HOST/$UWSGI_HOST/g" /etc/nginx/conf.d/mysite_nginx.conf
exec "$@"

Webサービスを起動するときは以下のコマンドを叩けばOKです。上がったらhttp://localhost/admin|Djangoの管理者サイトにアクセスできるはずです。

docker-compose up -d

すべてきれいさっぱり消すときは...(永続データも消えるのでちゅうい)

docker-compose down -v

Docker for WindowsでMySQLコンテナのデータを永続化する

Docker for WindowsMySQLコンテナのデータを永続化する際、ホストディレクトリを/var/lib/mysqlに単純にマウントすると、MySQLコンテナ側で以下のようなエラーが出ます。

2018-11-18T10:02:32.761277Z 1 [ERROR] [MY-012646] [InnoDB] File ./ibdata1: 'open' returned OS error 71. Cannot
continue operation

このエラーを回避するにはDockerボリュームを使ってデータを永続化すると、うまくいくみたいです。具体的にはdocker-compose.ymlに以下のように記載して起動するとうまくいきます。

version: '3'

services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: mydb
      MYSQL_USER: myuser
      MYSQL_PASSWORD: password
    volumes:
    - "mysql_data:/var/lib/mysql"

volumes:
  mysql_data:
    driver: local

なぜかまでは調査できてませんが、Windows側のディレクトパーミッションとか関係あるのかしら。。。

既定のアプリをコマンドで変更する方法

Windows 10の10月24日のアップデートでGUI上から一部の既定のアプリを変更することができないようになっているみたいです。これに見事にはまり、.txtファイルの既定のアプリをメモ帳から変更できなくなってしまいました。。。

Microsoft側もこのことを認識しており、11月下旬に向けて修正を予定しているようです。 https://support.microsoft.com/en-us/help/4462933/windows-10-update-kb4462933

Symptom Workaround
After installing this update, some users cannot set Win32 program defaults for certain app and file type combinations using the Open with… command or Settings > Apps > Default apps. In some cases, Microsoft Notepad or other Win32 programs cannot be set as the default. In some cases, attempting to set application defaults again will succeed. Microsoft is working on a resolution and estimates a solution will be available in late November 2018.

11月下旬まで待ってられるか!という人は以下のようにして変更できるみたいです。

ftype txtfile="<テキストエディタのパス>" "%1"

OpenStackとoVirtで自宅クラウド環境を構築してみた

f:id:bbrfkr:20181104113638p:plain

お久しぶりです。4か月間ぐらい全くポストしていませんでしたが生きています。

何やっていたかというと、自宅環境をOpenStackによってプライベートクラウド化しておりました。これが完成しないと諸々の検証ができないので、ブログのポストより優先していたら4か月も経っていた感じです。

4か月間にためていたナレッジは少しずつ文章にしていくつもりですが、まずはどんなプライベートクラウド環境を構築していたのか紹介する記事を書こうと思います。自宅環境を構築したい人、している人の少しでも参考になれば幸いです。

目的

プライベートクラウド環境の目的ですが、以下の通りです。

  • 製品の検証(主にインフラの検証。ハイパバイザレベルでの検証も含む)
  • サービス提供(Wikiサイトなど)

従来はoVirtというOSSの仮想化基盤製品でこれらの目的をこなしていたわけですが、プライベートクラウド化することによって次のことを狙っていました。

  • 様々なサービスのAPIコールによるデプロイ

oVirtはあくまで仮想化基盤なので、あくまでもAmazon EC2やEBS相当の機能しか提供してくれません。昨今のクラウドネイティブなアーキテクチャの学習には不向きだったのです。またコンテナ中核技術であるKubernetesの利便性を最大限引き出すにはクラウドとの連携は必須となります。このためOpenStackによって自宅IaaSを構築しようという考えに至りました。

ただし、OpenStackでは柔軟な仮想マシン設定を行うことが難しくなり、KVMなどのハイパバイザレベルでの動作検証が難しくなります。したがって検証環境すべてをOpenStackに移行したわけではなく、一部リソースをoVirt用に残すようにしました。

アーキテクチャ(物理)

物理構成は以下の通りになります。OpenStack Controllerはロードバランサなどの補助サーバはoVirt上のVMとして構築します。

f:id:bbrfkr:20181104114922p:plain

  • Intel NUC Pentiumモデル x 1 (oVirt Engine用)
  • Intel NUC Corei3モデル x 3 (oVirt Node用)
    • Mem: 32GB/1マシン
    • SSD: 500GB M.2
    • HDD: 1TB
    • NIC: 1GbE x 4
  • Intel NUC Corei3モデル x 3 (OpenStack Compute用)
    • Mem: 32GB/1マシン
    • SSD: 500GB M.2
    • HDD: 1TB
    • NIC: 1GbE x 4

アーキテクチャ(仮想)

VMを含めた構成は以下の通りです。冗長化による性能向上を狙って、OpenStack環境はActive/Activeクラスタを構成しています。(Cinder Volumeを除く)

f:id:bbrfkr:20181104121209p:plain

構築したサービス

プライベートクラウドでは以下のIaaSサービスを提供しています。

  • oVirt
    通常の仮想化環境を提供します。KVM等のハイパバイザ自体を検証したいときはこっちを使います。VMやボリュームのプロビジョニングを行うことができ、通常の仮想化環境といってもAPIがついているので、Ansible等を用いてプロビジョニングを自動化することが可能です。

  • OpenStack Keystone
    AWSにおけるIAM相当の認証基盤を提供します。

  • OpenStack Swift
    AWSにおけるS3相当のオブジェクトストレージ機能を提供します。

  • OpenStack Glance
    AWSにおけるAMI相当のVMイメージストア機能を提供します。Swiftと連携してイメージをSwiftに保存するようにしています。

  • OpenStack Nova
    AWSにおけるEC2相当のVM機能を提供します。

  • OpenStack Neutron
    AWSにおけるVPC、ELB相当の仮想ネットワーク、ソフトウェアロードバランサー機能を提供します。

  • OpenStack Horizon
    AWSにおけるマネジメントコンソール相当のダッシュボード機能を提供します。

  • OpenStack Cinder
    AWSにおけるEBS相当のブロックストレージ機能を提供します。

  • OpenStack Heat
    AWSにおけるCloudFormation相当のオーケストレーション機能を提供します。

  • OpenStack Trove
    AWSにおけるRDS相当のRDBMS機能を提供します。

  • OpenStack Manila
    AWSにおけるEFS相当の共有ファイルシステム機能を提供します。

  • OpenStack Designate
    AWSにおけるRoute53相当のDNS機能を提供します。

  • OpenStack Magnum
    AWSにおけるECS、EKS相当のコンテナオーケストレーション機能を提供します。Kubernetesのデプロイを一発で行えます。

まとめ

OSSを使って自宅環境をプライベートクラウド化してみましたが、ソフトウェアでインフラを制御するのはとても便利で快感です! お時間ある方は是非チャレンジしてみてはいかがでしょうか? 設計や構築方法について疑問がある方は質問していただければ可能な限りお答えしたいと思います!