自宅のPCにkubernetesとjenkinsをインストールしてみた

January 01, 2019

最近、kubernetesを自宅PCにインストールしてみました。まだ使い始めて間もないですが、自動的にコンテナをクラスタに配置してくれたり、コンテナ間を接続するためのネットワークの設定をしてくれたりするので、複数PCを持っていると重宝するかもしれません。

インストールする自宅PCですが、今回は2台使用しました。1台はKVMをインストールしたノートPC、もう一台はWindowsのデスクトップPCです。ノートPCにはmaster、node、それぞれに対応するインスタンス(CentOS7)を作成し、kubernetesをインストールしました。デスクトップPCにはVirtualBoxの仮想マシン(CentOS7)をnodeとして作成しました。kubernetesのインストールはこちらを参考にさせていただきました。なお、selinux、firewalldは下記のようにしていずれも無効にしています(master、node共通)。

# setenforce 0
# sed -i --follow-symlinks 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux
# systemctl stop firewalld
# systemctl disable firewalld

それぞれのインスタンスに割り当てたりソースですが、CPUは2つ、メモリはmasterに2G、nodeにそれぞれ4Gバイトを割り当てました。

また、kubernetesのパッケージ管理ツールであるhelmと、外部にWEBサーバ(今回はJenkins)を公開するためnginxベースのingress controllerもインストールしました。

helmのインストールは上記サイトからダウンロードし、masterの/usr/local/binに置きました。helm initでtillerというサーバコンポーネントがインストールされてセットアップが完了しますが、helm listなどを実行すると

Error: configmaps is forbidden: User "system:serviceaccount:kube-system:default" cannot list resource "configmaps" in API group "" in the namespace "kube-system"

のようなエラーが出ました。詳しくはわかりませんが、tiller-deployを実行するサービスアカウントがデフォルトではkube-system(名前空間の一つ)のdefaultになり、それがconfigmap(設定情報を管理するところ)にアクセスする権限がない、というエラーのようです。今回はこちらの情報を参考にして、tiller専用のサービスアカウントを作成しました。

ingress controllerについては、こちらを参考に、公開されているインストール用のyaml(mandatory.yamlservice-nodeport.yaml)をkubectlに指定してapplyしました(service-nodeport.yamlにはmasterのIPアドレスを指定)。

また、JenkinsはPersistent Volume Claim(Podからのストレージのリクエスト。これにより動的にクラスタ内のストレージが割り当てられる)でPersistent Volume(クラスタ内で提供するディスクの実体)を取得するため、こちらのサイトこちらのサイトを参考に、下記のようなyamlを作成し、nfsを使用したPersistent Volumeとそれに割り当てるStorage Class(ディスクのスペックみたいなもので動的に割り当てるPVに指定する必要がある)、及びPVの提供(プロビジョニング)を行うためのサービスアカウントのRBAC(ロールベースのアクセス制御)の設定用yamlを作成しました。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs
provisioner: myhost.net/nfs
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: myhost.net/nfs
            - name: NFS_SERVER
              value: nfs.myhost.net
            - name: NFS_PATH
              value: /volume/kuberdisk
      volumes:
        - name: nfs-client-root
          nfs:
            server: nfs.myhost.net
            path: /volume/kuberdisk
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io

---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

作成したStorage Classは下記のコマンドでデフォルトにしました。

kubectl patch storageclass nfs -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

次にJenkinsのインストールです。helm install stable/jenkinsだけでほぼ全自動でインストールされます。インストールが完了すると、パスワードの取得方法などのメッセージが出力されますので、あとはそれに従うだけです。ただ、このままではまだJenkinsのページに外部からアクセスできません。外部からのアクセスをkubernetes内部のIPアドレスに橋渡しするための設定が必要になります。haproxyのようなリバースプロキシ を自前で設置して設定する方法もありますが、今回は先ほどインストールしたingress controllerを使用しました。こちらを使うと簡単にサーバを公開できます。今回は、下記のようなyamlを作成して公開しました。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-conf-for-jenkins
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  rules:
  - host: jenkins.myhost.net
    http:
      paths:
      - path: /
        backend:
          serviceName: knotted-anaconda-jenkins
          servicePort: 8080

公開といっても、自宅のLAN内からしかアクセスしませんので、7行目でsslによるリダイレクト(デフォルトではhttpがhttpsにリダイレクトされる)を無効にしています。また、今後、公開したいサーバが増えた時のために、ホストのFQDNによってアクセス先を変えたかったので、hostも指定しています(10行目)。jenkins.myhost.netはmasterインスタンスのFQDNで、LAN内専用のDNSサーバのAレコードでmasterのIPアドレスを割り当てました。