ビビリフクロウの足跡

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

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

kubeadmを用いたKubernetes HAクラスタ on AWS(v.1.11対応版)

前の記事を公開してすぐにv.1.11が出てしまい、構築方法が変わったので、泣きながら再検証...

構成

  • OS: CentOS7
  • サーバ群
    • Master + etcd
      • 3台(aws-k8s-master[01-03])
    • Node
      • 3台(aws-k8s-node[01-03])

前提

  • 作業ユーザはroot

手順

NLBの作成(AWS ManagementConsole等)

(6443/TCPのリスナを作成、aws-k8s-master[01-03]のIPをターゲットグループに登録する)

Dockerのインストール(aws-k8s-master[01-03]、aws-k8s-node[01-03])

yum install -y docker
systemctl enable docker && systemctl start docker

kubeadm、kubelet、kubectlのインストール(aws-k8s-master[01-03]、aws-k8s-node[01-03])

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config
setenforce 0
yum install -y kubelet kubeadm kubectl
systemctl enable kubelet && systemctl start kubelet
cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system

最初のMasterサーバの構築(aws-k8s-master01)

cat <<EOF > kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1alpha2
kind: MasterConfiguration
kubernetesVersion: v1.11.0
apiServerCertSANs:
- "<NLBのDNS名>"
api:
  controlPlaneEndpoint: "<NLBのDNS名>:6443"
etcd:
  local:
    extraArgs:
      listen-client-urls: "https://127.0.0.1:2379,https://<aws-k8s-master01のIP>:2379"
      advertise-client-urls: "https://<aws-k8s-master01のIP>:2379"
      listen-peer-urls: "https://<aws-k8s-master01のIP>:2380"
      initial-advertise-peer-urls: "https://<aws-k8s-master01のIP>:2380"
      initial-cluster: "aws-k8s-master01=https://<aws-k8s-master01のIP>:2380"
    serverCertSANs:
      - aws-k8s-master01
      - <aws-k8s-master01のIP>
    peerCertSANs:
      - aws-k8s-master01
      - <aws-k8s-master01のIP>
networking:
  podSubnet: "192.168.0.0/16"
EOF
kubeadm init --config kubeadm-config.yaml
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

SSHキーペアの作成(aws-k8s-master02, aws-k8s-master03)

ssh-keygen -t rsa -b 4096 -C ""
cat ~/.ssh/id_rsa.pub
# 出力された内容をaws-k8s-master01の/root/.ssh/authorized_keysのエントリを追加する

証明書群、設定ファイル群の配布(aws-k8s-master02、aws-k8s-master03)

mkdir -p /etc/kubernetes/pki/etcd
scp root@<aws-k8s-master01のIP>:/etc/kubernetes/pki/ca.crt /etc/kubernetes/pki/ca.crt
scp root@<aws-k8s-master01のIP>:/etc/kubernetes/pki/ca.key /etc/kubernetes/pki/ca.key
scp root@<aws-k8s-master01のIP>:/etc/kubernetes/pki/sa.key /etc/kubernetes/pki/sa.key
scp root@<aws-k8s-master01のIP>:/etc/kubernetes/pki/sa.pub /etc/kubernetes/pki/sa.pub
scp root@<aws-k8s-master01のIP>:/etc/kubernetes/pki/front-proxy-ca.crt /etc/kubernetes/pki/front-proxy-ca.crt
scp root@<aws-k8s-master01のIP>:/etc/kubernetes/pki/front-proxy-ca.key /etc/kubernetes/pki/front-proxy-ca.key
scp root@<aws-k8s-master01のIP>:/etc/kubernetes/pki/etcd/ca.crt /etc/kubernetes/pki/etcd/ca.crt
scp root@<aws-k8s-master01のIP>:/etc/kubernetes/pki/etcd/ca.key /etc/kubernetes/pki/etcd/ca.key
scp root@<aws-k8s-master01のIP>:/etc/kubernetes/admin.conf /etc/kubernetes/admin.conf

2台目のMasterサーバの構築(aws-k8s-master02)

cat <<EOF > kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1alpha2
kind: MasterConfiguration
kubernetesVersion: v1.11.0
apiServerCertSANs:
- "<NLBのDNS名>"
api:
  controlPlaneEndpoint: "<NLBのDNS名>:6443"
etcd:
  local:
    extraArgs:
      listen-client-urls: "https://127.0.0.1:2379,https://<aws-k8s-master02のIP>:2379"
      advertise-client-urls: "https://<aws-k8s-master02のIP>:2379"
      listen-peer-urls: "https://<aws-k8s-master02のIP>:2380"
      initial-advertise-peer-urls: "https://<aws-k8s-master02のIP>:2380"
      initial-cluster: "aws-k8s-master01=https://<aws-k8s-master01のIP>:2380,aws-k8s-master02=https://<aws-k8s-master02のIP>:2380"
      initial-cluster-state: existing
    serverCertSANs:
      - aws-k8s-master02
      - <aws-k8s-master02のIP>
    peerCertSANs:
      - aws-k8s-master02
      - <aws-k8s-master02のIP>
networking:
  podSubnet: "192.168.0.0/16"
EOF
kubeadm alpha phase certs all --config kubeadm-config.yaml
kubeadm alpha phase kubelet config write-to-disk --config kubeadm-config.yaml
kubeadm alpha phase kubelet write-env-file --config kubeadm-config.yaml
kubeadm alpha phase kubeconfig kubelet --config kubeadm-config.yaml
systemctl start kubelet
CP0_IP=<aws-k8s-master01のIP>
CP0_HOSTNAME=aws-k8s-master01
CP1_IP=<aws-k8s-master02のIP>
CP1_HOSTNAME=aws-k8s-master02

KUBECONFIG=/etc/kubernetes/admin.conf kubectl exec -n kube-system etcd-${CP0_HOSTNAME} -- etcdctl --ca-file /etc/kubernetes/pki/etcd/ca.crt --cert-file /etc/kubernetes/pki/etcd/peer.crt --key-file /etc/kubernetes/pki/etcd/peer.key --endpoints=https://${CP0_IP}:2379 member add ${CP1_HOSTNAME} https://${CP1_IP}:2380
kubeadm alpha phase etcd local --config kubeadm-config.yaml
kubeadm alpha phase kubeconfig all --config kubeadm-config.yaml
kubeadm alpha phase controlplane all --config kubeadm-config.yaml
kubeadm alpha phase mark-master --config kubeadm-config.yaml

3台目のMasterサーバの構築(aws-k8s-master03)

cat <<EOF > kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1alpha2
kind: MasterConfiguration
kubernetesVersion: v1.11.0
apiServerCertSANs:
- "<NLBのDNS名>"
api:
  controlPlaneEndpoint: "<NLBのDNS名>:6443"
etcd:
  local:
    extraArgs:
      listen-client-urls: "https://127.0.0.1:2379,https://<aws-k8s-master03のIP>:2379"
      advertise-client-urls: "https://<aws-k8s-master03のIP>:2379"
      listen-peer-urls: "https://<aws-k8s-master03のIP>:2380"
      initial-advertise-peer-urls: "https://<aws-k8s-master03のIP>:2380"
      initial-cluster: "aws-k8s-master01=https://<aws-k8s-master01のIP>:2380,aws-k8s-master02=https://<aws-k8s-master02のIP>:2380,aws-k8s-master03=https://<aws-k8s-master03のIP>:2380"
      initial-cluster-state: existing
    serverCertSANs:
      - aws-k8s-master03
      - <aws-k8s-master03のIP>
    peerCertSANs:
      - aws-k8s-master03
      - <aws-k8s-master03のIP>
networking:
  podSubnet: "192.168.0.0/16"
EOF
kubeadm alpha phase certs all --config kubeadm-config.yaml
kubeadm alpha phase kubelet config write-to-disk --config kubeadm-config.yaml
kubeadm alpha phase kubelet write-env-file --config kubeadm-config.yaml
kubeadm alpha phase kubeconfig kubelet --config kubeadm-config.yaml
systemctl start kubelet
CP0_IP=<aws-k8s-master01のIP>
CP0_HOSTNAME=aws-k8s-master01
CP2_IP=<aws-k8s-master03のIP>
CP2_HOSTNAME=aws-k8s-master03

KUBECONFIG=/etc/kubernetes/admin.conf kubectl exec -n kube-system etcd-${CP0_HOSTNAME} -- etcdctl --ca-file /etc/kubernetes/pki/etcd/ca.crt --cert-file /etc/kubernetes/pki/etcd/peer.crt --key-file /etc/kubernetes/pki/etcd/peer.key --endpoints=https://${CP0_IP}:2379 member add ${CP2_HOSTNAME} https://${CP2_IP}:2380
kubeadm alpha phase etcd local --config kubeadm-config.yaml
kubeadm alpha phase kubeconfig all --config kubeadm-config.yaml
kubeadm alpha phase controlplane all --config kubeadm-config.yaml
kubeadm alpha phase mark-master --config kubeadm-config.yaml

flannelのインストール(aws-k8s-master01)

curl -O https://raw.githubusercontent.com/coreos/flannel/v0.10.0/Documentation/kube-flannel.yml
sed -i 's/10.244.0.0/192.168.0.0/g' kube-flannel.yml
kubectl apply -f kube-flannel.yml

Nodeのクラスタ参加(aws-k8s-node[01-03])

kubeadm join --token <token> <NLBのDNS名>:6443 --discovery-token-ca-cert-hash sha256:<hash>
# <token>、<hash>はkubeadm initをaws-k8s-master01で実行した際に表示されたものを使用