{"id":860,"date":"2019-10-19T21:49:21","date_gmt":"2019-10-19T19:49:21","guid":{"rendered":"https:\/\/blog.unetresgrossebite.com\/?p=860"},"modified":"2019-10-20T13:11:21","modified_gmt":"2019-10-20T11:11:21","slug":"openshift-4-baremetal-deployment","status":"publish","type":"post","link":"https:\/\/blog.unetresgrossebite.com\/?p=860","title":{"rendered":"OpenShift 4 &#8211; Baremetal Deployment"},"content":{"rendered":"<p>Once again, quick post regarding OpenShift, today experimenting with the new installer, and OpenShift 4.<\/p>\n<p>First, let&#8217;s remind ourselves that OKD 4 has not yet been released. I would be using my RedHat account credentials pulling images. I usually refuse to touch anything that is not strictly open source (and freely distributed), though I would make an exception here, as I&#8217;ve been waiting for OpenShift 4 for almost a year now. Back when my first OpenShift PR got refused, due to their focus being on OpenShift 4, &#8230; Now I&#8217;m visiting customers for OpenShift 4, I need my own lab to experiment with.<\/p>\n<p><\/p>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><em><strong>Prepare Hardware<\/strong><\/em><\/h2>\n<p>Dealing with a baremetal deployment, we would need to prepare a subnet with its DHCP and PXE servers, a pair of LoadBalancers, and several instances for OpenShift itself.<br>The following would assume a VLAN was created, we would provide with isc-dhcp-server, tftpd-hpa, bind\/nsd and haproxy configuration snippets.<\/p>\n<p>OpenShift nodes would include a bootstrap node (only required during deployment, would be shut down afterwards), three master nodes, and as much worker nodes as we can allocate.<br>Bootstrap and master nodes should ship with 4 vCPU and 16G RAM at least, while workers could go with 2 vCPU and 8G RAM. Docs mention provisioning those node with at least 120G of disk storage, though this does not seem to be mandatory.<br>Those nodes would be running on top of KVM hypervisors.<\/p>\n<p><\/p>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><strong>Download Assets<\/strong><\/h2>\n<p>We would start downloading a few assets out of <a href=\"https:\/\/cloud.openshift.com\" target=\"_blank\" rel=\"noopener noreferrer\">RedHat cloud portal<\/a>.<\/p>\n<p>We would find links to RedHat CoreOS PXE sources &#8211; a kernel, an initramfs, and a pair of compressed filesystems that would be used installing CoreOS to our nodes. We would install those to our PXE server later.<\/p>\n<p>We would also fetch a pull secret, that would allow us downloading images out of RedHat and Quay registries.<\/p>\n<p>Finally, we would retrieve the latest oc client, as well as the openshift-install binaries.<\/p>\n<p><\/p>\n<h2 style=\"padding-left: 40px;\"><strong>DNS<\/strong><\/h2>\n<p>Next, we would prepare DNS records for our OpenShift cluster and nodes.<\/p>\n<p>Contrarily to OpenShift3, we would not be able to use customized names for the cluster API or its applications.&nbsp;<\/p>\n<p>We would first create a zone for cluster host names, <em>db.nodes.example.com<\/em>:<\/p>\n<blockquote>\n<p>$ORIGIN nodes.example.com.<br>bootstrap A 10.42.253.9<br>master1 A 10.42.253.10<br>master2 A 10.42.253.11<br>master3 A 10.42.253.12<br>infra1 A 10.42.253.13<br>infra2 A 10.42.253.14<br>infra3 A 10.42.253.15<br>compute1 A 10.42.253.20<br>compute2 A 10.42.253.21<br>compute3 A 10.42.253.22<br>compute4 A 10.42.253.23<br>compute5 A 10.42.253.24<br>haproxy1 A 10.42.253.150<br>haproxy2 A 10.42.253.151<\/p>\n<\/blockquote>\n<p>Next, we would create a zone for the cluster itself, <em>db.intra.example.com<\/em>:<\/p>\n<blockquote>\n<p>$ORIGIN intra.example.com.<br>api A 10.42.253.150<br>api A 10.42.253.151<br>api-int A 10.42.253.150<br>api-int A 10.42.253.151<br>*.apps A 10.42.253.150<br>*.apps A 10.42.253.151<br>etcd-0 A 10.42.253.10<br>etcd-1 A 10.42.253.11<br>etcd-2 A 10.42.253.12<br>_etcd-server-ssl._tcp 86400 IN SRV 0 10 2380 etcd-0.nodes.example.com.<br>_etcd-server-ssl._tcp 86400 IN SRV 0 10 2380 etcd-1.nodes.example.com.<br>_etcd-server-ssl._tcp 86400 IN SRV 0 10 2380 etcd-2.nodes.example.com.<\/p>\n<\/blockquote>\n<p>And corresponding reverse records, in <em>db.253.42.10.in-addr.arpa<\/em>:<\/p>\n<blockquote>\n<p>$ORIGIN 253.42.10.in-addr.arpa.<br>9 PTR bootstrap.nodes.example.com.<br>10 PTR master1.nodes.example.com.<br>11 PTR master2.nodes.example.com.<br>12 PTR master3.nodes.example.com.<br>13 PTR infra1.nodes.example.com.<br>14 PTR infra2.nodes.example.com.<br>15 PTR infra3.nodes.example.com.<br>20 PTR compute1.nodes.example.com.<br>21 PTR compute2.nodes.example.com.<br>22 PTR compute3.nodes.example.com.<br>23 PTR compute4.nodes.example.com.<br>24 PTR compute5.nodes.example.com.<br>150 PTR haproxy1.nodes.example.com.<br>151 PTR haproxy2.nodes.example.com.<\/p>\n<\/blockquote>\n<p>Don&#8217;t forget to reload your zones before going further.<\/p>\n<p><\/p>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><em><strong>DHCP<\/strong><\/em><\/h2>\n<p>Next, we would configure our DHCP server. First, we would setup static leases for our OpenShift nodes:<\/p>\n<blockquote>\n<p>host bootstrap-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:e1:48:6a;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.9;<br>}<br>host master0-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:be:c0:a4;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.10;<br>}<br>host master1-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:79:f3:0f;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.11;<br>}<br>host master2-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:69:74:8c;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.12;<br>}<br>host infra1-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:d3:40:dc;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.13;<br>}<br>host infra2-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:20:f0:af;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.14;<br>}<br>host infra3-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:81:83:25;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.15;<br>}<br>host compute1-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:48:77:48;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.20;<br>}<br>host compute2-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:88:94:94;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.20;<br>}<br>host compute3-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:ff:37:14;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.20;<br>}<br>host compute4-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:c7:46:2d;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.20;<br>}<br>host compute5-eth0 {<br>&nbsp;&nbsp;&nbsp;&nbsp;hardware ethernet 52:54:00:e1:60:5b;<br>&nbsp;&nbsp;&nbsp;&nbsp;fixed-address 10.42.253.20;<br>}<\/p>\n<\/blockquote>\n<p>Next, we would setup a subnet for OpenShift nodes, enabling with PXE booting options:<\/p>\n<blockquote>\n<p>subnet 10.42.253.0 netmask 255.255.255.0<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;option routers 10.42.253.1;<br>&nbsp;&nbsp;&nbsp;&nbsp;option domain-name &#8220;nodes.example.com intra.example.com&#8221;;<br>&nbsp;&nbsp;&nbsp;&nbsp;option domain-name-servers 10.42.253.3, 10.42.253.5;<br>&nbsp;&nbsp;&nbsp;&nbsp;filename &#8220;pxelinux.0&#8221;;<br>&nbsp;&nbsp;&nbsp;&nbsp;range 10.42.253.9 10.42.253.254;<br>&nbsp;&nbsp;&nbsp;&nbsp;next-server 10.42.44.100;<br>}<\/p>\n<\/blockquote>\n<p>Don&#8217;t forget to restart your DHCP server.<\/p>\n<p><\/p>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><em><strong>Ignition<\/strong><\/em><\/h2>\n<p>Now, we would generate some configurations to be served to PXE clients.<\/p>\n<p>First, we would create a configuration file, mandatory for baremetal deployments, install-config.yaml:<\/p>\n<blockquote>\n<p>apiVersion: v1<br>baseDomain: example.com<br>compute:<br>&#8211; hyperthreading: Enabled<br>&nbsp;&nbsp;name: worker<br>replicas: 0<br>controlPlane:<br>&nbsp;&nbsp;hyperthreading: Enabled<br>&nbsp;&nbsp;name: master<br>&nbsp;&nbsp;replicas: 3<br>metadata:<br>&nbsp;&nbsp;name: intra<br>networking:<br>&nbsp;&nbsp;clusterNetwork:<br>&nbsp;&nbsp;&#8211; cidr: 10.128.0.0\/14<br>&nbsp;&nbsp;&nbsp;&nbsp;hostPrefix: 23<br>&nbsp;&nbsp;networkType: OpenShiftSDN<br>&nbsp;&nbsp;serviceNetwork:<br>&nbsp;&nbsp;&#8211; 172.30.0.0\/16<br>platform:<br>&nbsp;&nbsp;none: {}<br>pullSecret: &lt;pull-secret-from-cloud.openshift.com&gt;<br>sshKey: &#8216;ssh-rsa &lt;some-public-key-of-yours&gt; admin@example.com&#8217;<\/p>\n<\/blockquote>\n<p>If you haven&#8217;t already, extract the openshift-install binary from the archive downloaded out of RedHat cloud portal.<\/p>\n<blockquote>\n<p>mkdir install-directory<br>cp -p install-config.yaml install-directory\/<br>.\/openshift-install create manifests &#8211;dir=.\/install-directory<br>sed -i &#8216;s|mastersSchedulable:.*|mastersSchedulable: false|&#8217; \\<br>&nbsp; &nbsp; .\/install-directory\/manifests\/cluster-scheduler-02-config.yaml<br>.\/openshift-install create ignition-configs &#8211;dir=.\/install-directory\/<br>scp -p install-directory\/*.ign root@pxe-server:\/srv\/tftpboot\/ocp4\/<\/p>\n<\/blockquote>\n<p>Note that the install-directory\/auth subfolder includes a kubeconfig file, that can be used with the oc and kubectl clients, querying our cluster API, as well as kubeadmin default password logging into the cluster console.<\/p>\n<p><\/p>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><em><strong>PXE<\/strong><\/em><\/h2>\n<p>Next, we would configure our PXE server booting RedHat CoreOS nodes.<\/p>\n<blockquote>\n<p>wget -o \/srv\/tftpboot\/ocp4\/kernel \\<br>&nbsp;&nbsp; https:\/\/mirror.openshift.com\/pub\/openshift-v4\/dependencies\/rhcos\/4.2\/4.2.0\/rhcos-4.2.0-x86_64-installer-kernel<br>wget -o \/srv\/tftpboot\/ocp4\/initrd \\<br>&nbsp;&nbsp; https:\/\/mirror.openshift.com\/pub\/openshift-v4\/dependencies\/rhcos\/4.2\/4.2.0\/rhcos-4.2.0-x86_64-installer-initramfs.img<br>wget -o \/srv\/tftpboot\/ocp4\/metalbios.raw.gz \\<br>&nbsp;&nbsp;https:\/\/mirror.openshift.com\/pub\/openshift-v4\/dependencies\/rhcos\/4.2\/4.2.0\/rhcos-4.2.0-x86_64-metal-bios.raw.gz<br>cat &lt;&lt;EOF &gt;\/srv\/tftproot\/boot-screens\/ocp4.cfg<br>menu title OCP4 RH-CoreOS Systems<br>&nbsp;&nbsp;menu title OCP4 RH-CoreOS Systems<br>&nbsp;&nbsp;&nbsp;&nbsp;menu label OCP4 RH-CoreOS Systems<br>&nbsp;&nbsp;&nbsp;&nbsp;menu exit<br>&nbsp;&nbsp;label &#8211;<br>&nbsp;&nbsp;&nbsp;&nbsp;menu label 4.2.0 x86_64 &#8211; bootstrap<br>&nbsp;&nbsp;&nbsp;&nbsp;kernel installers\/ocp4-rhcos-4.2.0\/x86_64\/linux<br>&nbsp;&nbsp;&nbsp;&nbsp;append initrd=installers\/ocp4-rhcos-4.2.0\/x86_64\/initrd-raw ip=dhcp rd.neednet=1 coreos.inst=yes coreos.inst.install_dev=vda coreos.inst.image_url=http:\/\/10.42.44.100\/ocp4\/rhcos-4.2.0-x86_64-metal-bios.raw.gz coreos.inst.ignition_url=http:\/\/10.42.44.100\/ocp4\/bootstrap.ign<br>&nbsp;&nbsp;label &#8211;<br>&nbsp;&nbsp;&nbsp;&nbsp;menu label 4.2.0 x86_64 &#8211; master<br>&nbsp;&nbsp;&nbsp;&nbsp;kernel installers\/ocp4-rhcos-4.2.0\/x86_64\/linux<br>&nbsp;&nbsp;&nbsp;&nbsp;append initrd=installers\/ocp4-rhcos-4.2.0\/x86_64\/initrd-raw ip=dhcp rd.neednet=1 coreos.inst=yes coreos.inst.install_dev=vda coreos.inst.image_url=http:\/\/10.42.44.100\/ocp4\/rhcos-4.2.0-x86_64-metal-bios.raw.gz coreos.inst.ignition_url=http:\/\/10.42.44.100\/ocp4\/master.ign<br>&nbsp;&nbsp;label &#8211;<br>&nbsp;&nbsp;&nbsp;&nbsp;menu label 4.2.0 x86_64 &#8211; worker<br>&nbsp;&nbsp;&nbsp;&nbsp;kernel installers\/ocp4-rhcos-4.2.0\/x86_64\/linux<br>&nbsp;&nbsp;&nbsp;&nbsp;append initrd=installers\/ocp4-rhcos-4.2.0\/x86_64\/initrd-raw ip=dhcp rd.neednet=1 coreos.inst=yes coreos.inst.install_dev=vda coreos.inst.image_url=http:\/\/10.42.44.100\/ocp4\/rhcos-4.2.0-x86_64-metal-bios.raw.gz coreos.inst.ignition_url=http:\/\/10.42.44.100\/ocp4\/worker.ign<br>menu end<br>EOF<\/p>\n<\/blockquote>\n<p>Note that our PXE server also includes its HTTP server, hosting ignition configs and CoreOS installation image URL. In theory, all you need here is an HTTP server, not necessarily related to your PXE server.<\/p>\n<p><\/p>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><em><strong>Load Balancers<\/strong><\/em><\/h2>\n<p>Before we can deploy OpenShift, we would setup its LoadBalancers. Here, we would use HAProxy, with the following configuration:<\/p>\n<blockquote>\n<p>global<br>&nbsp;&nbsp;maxconn 20000<br>&nbsp;&nbsp;log \/dev\/log local0 info<br>&nbsp;&nbsp;chroot \/var\/lib\/haproxy<br>&nbsp;&nbsp;pidfile \/var\/run\/haproxy.pid<br>&nbsp;&nbsp;user haproxy<br>&nbsp;&nbsp;group haproxy<br>&nbsp;&nbsp;daemon<br>&nbsp;&nbsp;stats socket \/var\/lib\/haproxy\/stats<\/p>\n<p>defaults<br>&nbsp;&nbsp;mode http<br>&nbsp;&nbsp;log global<br>&nbsp;&nbsp;option httplog<br>&nbsp;&nbsp;option dontlognull<br>&nbsp;&nbsp;option forwardfor except 127.0.0.0\/8<br>&nbsp;&nbsp;option redispatch<br>&nbsp;&nbsp;retries 3<br>&nbsp;&nbsp;timeout http-request 10s<br>&nbsp;&nbsp;timeout queue 1m<br>&nbsp;&nbsp;timeout connect 10s<br>&nbsp;&nbsp;timeout client 300s<br>&nbsp;&nbsp;timeout server 300s<br>&nbsp;&nbsp;timeout http-keep-alive 10s<br>&nbsp;&nbsp;timeout check 10s<br>&nbsp;&nbsp;maxconn 20000<\/p>\n<p>listen stats<br>&nbsp;&nbsp;bind :9000<br>&nbsp;&nbsp;mode http<br>&nbsp;&nbsp;stats enable<br>&nbsp;&nbsp;stats uri \/<\/p>\n<p>frontend k8s-api<br>&nbsp;&nbsp;bind *:6443<br>&nbsp;&nbsp;default_backend k8s-api<br>&nbsp;&nbsp;mode tcp<br>&nbsp;&nbsp;option tcplog<\/p>\n<p>backend k8s-api<br>&nbsp;&nbsp;balance source<br>&nbsp;&nbsp;mode tcp<br>&nbsp;&nbsp;server bootstrap 10.42.253.9:6443 check<br>&nbsp;&nbsp;server master0 10.42.253.10:6443 check<br>&nbsp;&nbsp;server master1 10.42.253.11:6443 check<br>&nbsp;&nbsp;server master2 10.42.253.12:6443 check<\/p>\n<p>frontend machine-config-server<br>&nbsp;&nbsp;bind *:22623<br>&nbsp;&nbsp;default_backend machine-config-server<br>&nbsp;&nbsp;mode tcp<br>&nbsp;&nbsp;option tcplog<\/p>\n<p>backend machine-config-server<br>&nbsp;&nbsp;balance source<br>&nbsp;&nbsp;mode tcp<br>&nbsp;&nbsp;server bootstrap 10.42.253.9:22623 check<br>&nbsp;&nbsp;server master0 10.42.253.10:22623 check<br>&nbsp;&nbsp;server master1 10.42.253.11:22623 check<br>&nbsp;&nbsp;server master2 10.42.253.12:22623 check<\/p>\n<p>frontend apps-tls<br>&nbsp;&nbsp;bind *:443<br>&nbsp;&nbsp;default_backend apps-tls<br>&nbsp;&nbsp;mode tcp<br>&nbsp;&nbsp;option tcplog<\/p>\n<p>backend apps-tls<br>&nbsp;&nbsp;balance source<br>&nbsp;&nbsp;mode tcp<br>&nbsp;&nbsp;server router0 10.42.253.13:443 check<br>server router1 10.42.253.14:443 check<br>&nbsp;&nbsp;server router2 10.42.253.15:443 check<\/p>\n<p>frontend apps-clear<br>&nbsp;&nbsp;bind *:80<br>&nbsp;&nbsp;default_backend apps-clear<br>&nbsp;&nbsp;mode tcp<br>&nbsp;&nbsp;option tcplog<\/p>\n<p>backend apps-clear<br>&nbsp;&nbsp;balance source<br>&nbsp;&nbsp;mode tcp<br>&nbsp;&nbsp;server router0 10.42.253.13:80 check<br>&nbsp;&nbsp;server router1 10.42.253.14:80 check<br>&nbsp;&nbsp;server router2 10.42.253.15:80 check<\/p>\n<\/blockquote>\n<p>Don&#8217;t forget to start and enable HAProxy service.<\/p>\n<p><\/p>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><em><strong>Boot Instances<\/strong><\/em><\/h2>\n<p>Now we should have everything we need. First boot the boostrap node using PXE, wait for it to reboot, then boot the three master nodes in PXE.<\/p>\n<p>We would be able to SSH to each node, as the <em>core<\/em> user, using the SSH key passed to openshift-install earlier. Keep an eye on system logs.<\/p>\n<p>Meanwhile, we could use openshift-install tracking for OpenShift API bootstrap completion:<\/p>\n<blockquote>\n<p>.\/openshift-install &#8211;dir=.\/install-directory wait-for bootstrap-complete \\<br>&nbsp;&nbsp;&nbsp;&nbsp;log-level info<\/p>\n<\/blockquote>\n<p>Eventually, that command would exit, and should confirm our cluster API is now reachable. At that stage, the cluster is not yet done deploying, though we&#8217;re getting close.<\/p>\n<p>Next, we would boot our infra nodes in PXE. Keep an eye on certificate signing requests, as we would need to approve those new nodes while joining the cluster:<\/p>\n<blockquote>\n<p>oc get csr<br>oc adm certificate sign csr-xxx<\/p>\n<\/blockquote>\n<p>Eventually, we should be able to confirm the cluster operators are finishing to deploy.<\/p>\n<p>The only one that would stay in a degraded state would be the image registry operator. Here, we would need to define OpenShift integrated registry storage configuration:<\/p>\n<blockquote>\n<p>oc edit configs.imageregistry.operator.openshift.io<\/p>\n<\/blockquote>\n<p>To keep it simple, we would stick to an emptyDir storage (volatile), which is not usually recommended.<\/p>\n<blockquote>\n<p>oc get co<br>NAME VERSION AVAILABLE PROGRESSING DEGRADED SINCE<br>authentication 4.2.0 True False False 1h36m<br>cloud-credential 4.2.0 True False False 2h<br>cluster-autoscaler 4.2.0 True False False 1h56m<br>console 4.2.0 True False False 1h37m<br>dns 4.2.0 True False False 2h<br>image-registry 4.2.0 True False False 49m<br>ingress 4.2.0 True False False 1h42m<br>insights 4.2.0 True False False 2h<br>kube-apiserver 4.2.0 True False False 1h59m<br>kube-controller-manager 4.2.0 True False False 1h58m<br>kube-scheduler 4.2.0 True False False 1h59m<br>machine-api 4.2.0 True False False 2h<br>machine-config 4.2.0 True False False 2h<br>marketplace 4.2.0 True False False 1h56m<br>monitoring 4.2.0 True False False 1h40m<br>network 4.2.0 True False False 2h<br>node-tuning 4.2.0 True False False 1h56m<br>openshift-apiserver 4.2.0 True False False 1h57m<br>openshift-controller-manager 4.2.0 True False False 1h59m<br>openshift-samples 4.2.0 True False False 1h55m<br>operator-lifecycle-manager 4.2.0 True False False 2h<br>operator-lifecycle-manager-catalog 4.2.0 True False False 2h<br>operator-lifecycle-manager-packageserver 4.2.0 True False False 1h58m<br>service-ca 4.2.0 True False False 2h<br>service-catalog-apiserver 4.2.0 True False False 1h56m<br>service-catalog-controller-manager 4.2.0 True False False 1h57m<br>storage 4.2.0 True False False 1h56m<\/p>\n<\/blockquote>\n<p>Eventually, we may boot workers using PXE, until all nodes joined our cluster. We can also terminate the bootstrap node, that is no longer needed.<\/p>\n<p><\/p>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><em><strong>LDAP Authentication<\/strong><\/em><\/h2>\n<p>Finally, we would setup LDAP authentication. By default, OpenShift4 ships with a single kubeadmin user, that could be used during initial cluster configuration.<\/p>\n<blockquote>\n<p>oc &#8211;config .\/kubeconfig create secret generic ldap-secret \\<br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211;from-literal=bindPassword=&lt;secret&gt; -n openshift-config<br>oc &#8211;config .\/kubeconfig create configmap ldap-ca \\<br>&nbsp;&nbsp;&nbsp;&nbsp;&#8211;from-file=ca.crt=\/path\/to\/ldap-ca-chain.crt -n openshift-config<\/p>\n<\/blockquote>\n<p>Having create a Secret with our OpenShift LDAP service account bind password, and a ConfigMap serving the CA chain, used to sign our OpenLDAP TLS certificate, we would then import the following OAuth configuration:<\/p>\n<blockquote>\n<p>apiVersion: config.openshift.io\/v1<br>kind: OAuth<br>metadata:<br>&nbsp;&nbsp;name: cluster<br>spec:<br>&nbsp;&nbsp;identityProviders:<br>&nbsp;&nbsp;&#8211; name: LDAP<br>&nbsp;&nbsp;&nbsp;&nbsp;mappingMethod: claim <br>&nbsp;&nbsp;&nbsp;&nbsp;type: LDAP<br>&nbsp;&nbsp;&nbsp;&nbsp;ldap:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;attributes:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8211; dn<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;email: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8211; mail<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8211; sn<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;preferredUsername: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8211; uid<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bindDN: &#8220;cn=openshift,ou=services,dc=example,dc=com&#8221; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bindPassword: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: ldap-secret<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ca: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: ldap-ca<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insecure: false <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url: &#8220;ldaps:\/\/netserv.vms.example.com\/ou=users,dc=example,dc=com?uid?sub?(&amp;(objectClass=inetOrgPerson)(!(pwdAccountLockedTime=*)))&#8221;<\/p>\n<\/blockquote>\n<p>Having applied that configuration, we would see Pods from the openshift-authentication namespace rebooting. We would then be able to log in using an LDAP account.<\/p>\n<div id=\"attachment_864\" style=\"width: 1034px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/blog.unetresgrossebite.com\/wp-content\/uploads\/2019\/10\/dashboard-ocp4.png\"><img aria-describedby=\"caption-attachment-864\" decoding=\"async\" loading=\"lazy\" class=\"size-large wp-image-864\" src=\"https:\/\/blog.unetresgrossebite.com\/wp-content\/uploads\/2019\/10\/dashboard-ocp4-1024x522.png\" alt=\"OpenShift4 Dashboard\" width=\"1024\" height=\"522\" srcset=\"https:\/\/blog.unetresgrossebite.com\/wp-content\/uploads\/2019\/10\/dashboard-ocp4-1024x522.png 1024w, https:\/\/blog.unetresgrossebite.com\/wp-content\/uploads\/2019\/10\/dashboard-ocp4-300x153.png 300w, https:\/\/blog.unetresgrossebite.com\/wp-content\/uploads\/2019\/10\/dashboard-ocp4-768x391.png 768w, https:\/\/blog.unetresgrossebite.com\/wp-content\/uploads\/2019\/10\/dashboard-ocp4.png 1884w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><p id=\"caption-attachment-864\" class=\"wp-caption-text\">OpenShift4 Dashboard<\/p><\/div>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><em><strong>Infra Nodes<\/strong><\/em><\/h2>\n<p>Last detail: after deployment, an OpenShift 4 cluster would include master and worker nodes, while OpenShift 3 used to ship with master, infra and compute nodes.<\/p>\n<p>The worker nodes in OpenShift 4 are meant to replace both infra and computes, which could make sense running smaller setups, though I would argue is not much practical scaling out. Having a small set of nodes, designated to host OpenShift ingress controllers is a good thing, as we only need to configure those IPs as backends for our applications loadbalancers. Say we only rely on worker nodes, every time we add new members to our cluster, we would also need reconfiguring our loadbalancer.<\/p>\n<p>Hence, we would create a group of Infra machines, starting with creating a MachineConfigPool, using the following cofiguration:<\/p>\n<blockquote>\n<p>apiVersion: machineconfiguration.openshift.io\/v1<br>kind: MachineConfigPool<br>metadata:<br>&nbsp;&nbsp;name: infra<br>spec:<br>&nbsp;&nbsp;machineConfigSelector:<br>&nbsp;&nbsp;&nbsp;&nbsp;matchLabels:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;machineconfiguration.openshift.io\/role: infra<br>&nbsp;&nbsp;nodeSelector:<br>&nbsp;&nbsp;&nbsp;&nbsp;matchLabels:<br>&nbsp;&nbsp;&nbsp;&nbsp;node-role.kubernetes.io\/infra: &#8220;&#8221;<br>&nbsp;&nbsp;paused: false<\/p>\n<\/blockquote>\n<p>Having applied that configuration, we would then dump MachineConfig objects applying to worker nodes:<\/p>\n<blockquote>\n<p>DUMP=$(oc get machineconfig | grep -v rendered | \\<br>\n&nbsp;&nbsp;awk &#8216;\/worker\/{print $1}&#8217; | tr &#8216;\\n&#8217; &#8216; &#8216;)<\/p>\n<p>oc get machineconfig -o yaml $DUMP &gt;machineconfig-infra.yaml\n<\/p>\n<\/blockquote>\n<p>We would then edit <i>machineconfig-infra.yaml<\/i> content, removing &#8220;generated-by&#8221; annotations, creationTimestamps, generation, ownerReferences, resourceVersions, selfLink and uid metadata. Replace any remaning mention of &#8220;worker&#8221; by &#8220;infra&#8221;. Then apply the resulting objects:<\/p>\n<blockquote>\n<p>oc apply -f machineconfig-infra.yaml<br>\noc get mc<br>\n00-infra 2.2.0 1m<br>\n01-infra-container-runtime 2.2.0 1m<br>\n01-infra-kubelet 2.2.0 1m<br>\n99-infra-ad9f8790-f270-11e9-a34e-525400e1605b-registries 2.2.0 1m<br>\n99-infra-ssh 2.2.0  1m\n<\/p>\n<\/blockquote>\n<p>At that stage, the MachineConfig Operator should be rendering a last MachineConfig object, including an exhaustive list of configurations for our infra nodes. Once <i>oc get mc<\/i> includes that rendered configuration, we would make sure the MachineConfig Operator is done with our MachineConfigPool and start re-labeling nodes accordingly:<\/p>\n<blockquote>\n<p>oc get mcp<br>NAME CONFIG UPDATED UPDATING DEGRADED<br>infra rendered-infra-0506920a222781a19fff88a4196deef4 True False False<br>master rendered-master-747943425e64364488e51d15e5281265 True False False<br>worker rendered-worker-5e70256103cc4d0ce0162430de7233a1 True False False<br>oc label node infra1.nodes.example.com node-role.kubernetes.io\/infra=<br>\nnode\/infra1.nodes.example.com labeled<br>oc label node infra1.nodes.example.com node-role.kubernetes.io\/worker-<br>\nnode\/infra1.nodes.example.com labeled<\/p>\n<\/blockquote>\n<p>From there, our node would be set unschedulable, drained, and rebooted. Our customized MachineConfig should have changed the role label applied when our node boots, which we may confirm once it is done restarting<\/p>\n<blockquote>\n<p>oc get nodes<br>\ncompute1.nodes.example.com Ready worker 47m v1.14.6+c07e432da<br>\ncompute2.nodes.example.com Ready worker 45m v1.14.6+c07e432da<br>\ncompute3.nodes.example.com Ready worker 34m v1.14.6+c07e432da<br>\ncompute4.nodes.example.com Ready worker 33m v1.14.6+c07e432da<br>\ncompute5.nodes.example.com Ready worker 31m v1.14.6+c07e432da<br>\ninfra1.nodes.example.com Ready infra 2h v1.14.6+c07e432da<br>\ninfra2.nodes.example.com Ready worker 2h v1.14.6+c07e432da<br>\ninfra3.nodes.example.com Ready worker 2h v1.14.6+c07e432da<br>\nmaster1.nodes.example.com Ready master 2h v1.14.6+c07e432da<br>\nmaster2.nodes.example.com Ready master 2h v1.14.6+c07e432da<br>\nmaster3.nodes.example.com Ready master 2h v1.14.6+c07e432da\n<\/p>\n<\/blockquote>\n<p>Once our node is back, we would proceed with the next infra node.<\/p>\n<p>We would eventually reconfigure our Ingress Controller deploying OpenShift Routers back to our infra nodes:<\/p>\n<blockquote>\n<p>oc edit -n openshift-ingress-operator ingresscontroller default<br>\nspec:<br>&nbsp;&nbsp;nodePlacement:<br>&nbsp;&nbsp;&nbsp;&nbsp;nodeSelector:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;matchLabels:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node-role.kubernetes.io\/infra: &#8220;&#8221;<br>&nbsp;&nbsp;replicas: 3<\/p>\n<\/blockquote>\n<p>We would then keep track of routers Pods as they&#8217;re being re-deployed:<\/p>\n<blockquote>\n<p>\noc get pods -n openshift-ingress -o wide<br>\nNAME READY STATUS RESTARTS AGE IP NODE<br>router-default-86cdb97784-4d72k 1\/1 Running 0 14m 10.42.253.14 infra2.nodes.example.com<br>\nrouter-default-86cdb97784-8f5vm 1\/1 Running 0 14m 10.42.253.15 infra3.nodes.example.com<br>router-default-86cdb97784-bvvdc 1\/1 Running 0 105s 10.42.253.13 infra1.nodes.example.com<\/p>\n<\/blockquote>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><em><strong>Ceph RBD Storage<\/strong><\/em><\/h2>\n<p>Later on, we may want to configure OpenShift interfacing with an existing Ceph cluster, setting up persisting volumes.<\/p>\n<p>While OpenShift 3 used to ship with <i>rbd<\/i> binaries in the api controller image, while allowing for their installation on OpenShift nodes, this is no longer the case with OpenShift 4. Instead, we would rely on CSI (Container Storage Interface), which is meant to be a more generic interface.<\/p>\n<p>Then, we would need to deploy Ceph CSI interface to OpenShift,<\/p>\n<blockquote>\n<p>git clone https:\/\/github.com\/ceph\/ceph-csi\/<br>oc new-project ceph-csi<br>for sa in rbd-csi-provisioner rbd-csi-nodeplugin; do<br>\n&nbsp;&nbsp;&nbsp;&nbsp;oc create sa $sa<br>&nbsp;&nbsp;&nbsp;&nbsp;oc adm policy add-scc-to-user hostaccess system:serviceaccount:ceph-csi:$sa<br>&nbsp;&nbsp;&nbsp;&nbsp;oc adm policy add-scc-to-user privileged system:serviceaccount:ceph-csi:$sa<br>done<br>\ncat ceph-csi\/deploy\/rbd\/kubernetes\/v1.14+\/csi-*yaml | sed &#8216;s|namespace: default|namespace: ceph-csi|g&#8217; | oc apply -n ceph-csi -f-<br>cat &lt;&lt;EOF &gt;config.json<br>[<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&#8220;clusterID&#8221;: &#8220;my-ceph-cluster-id&#8221;,<br>&nbsp;&nbsp;&nbsp;&nbsp;&#8220;monitors: [ &#8220;10.1.2.3&#8221;,&#8221;10.1.2.4&#8243;10.1.2.5&#8243; ]<br>&nbsp;&nbsp;}<br>]<br>EOF<br>oc delete cm -n ceph-csi ceph-csi-config<br>oc create cm -n ceph-csi ceph-csi-config &#8211;from-file=config.json=.\/config.json<br>cat &lt;&lt; EOF &gt;secret.yaml<br>apiVersion: v1<br>kind: Secret<br>metadata:<br>&nbsp;&nbsp;name: ceph-rbd-secret<br>stringData:<br>&nbsp;&nbsp;userID: my-ceph-user-id<br>&nbsp;&nbsp;userKey: my-user-key<br>EOF<br>oc apply -n default -f secret.yaml<br>cat &lt;&lt; EOF &gt;storageclass.yaml<br>apiVersion: storage.k8s.io\/v1<br>kind: StorageClass<br>metadata:<br>&nbsp;&nbsp;name: ceph-storage<br>provisioner: rbd.csi.ceph.com<br>parameters:<br>&nbsp;&nbsp;clusterID: my-ceph-cluster-id<br>&nbsp;&nbsp;pool: kube<br>&nbsp;&nbsp;imageFeatures: layering<br>&nbsp;&nbsp;csi.storage.k8s.io\/provisioner-secret-name: csi-rbd-secret<br>&nbsp;&nbsp;csi.storage.k8s.io\/provisioner-secret-namespace: default<br>&nbsp;&nbsp;csi.storage.k8s.io\/node-stage-secret-name: csi-rbd-secret<br>&nbsp;&nbsp;csi.storage.k8s.io\/node-stage-secret-namespace: default<br>&nbsp;&nbsp;csi.storage.k8s.io\/fstype: xfs<br>reclaimPolicy: Delete<br>mountOptions:<br>&#8211; discard<br>EOF<br>oc apply -f storageclass.yaml<\/p>\n<\/blockquote>\n<p>At that stage, we would have deployed a DaemonSet of <i>csi-rbdplugin<\/i> Pods, tasked with attaching and detaching volumes during Pods scheduling and terminations, as well as a Deployment of <i>csi-rbdplugin-provisioner<\/i> Pods, creating and purging volumes out of Ceph, while managing OpenShift Persistent Volumes.<\/p>\n<p>At that stage, we may create a first Persistent Volume and redeploy OpenShift integrated registry on top of it:<\/p>\n<blockquote>\n<p>cat &lt;&lt;EOF &gt;registry-pvc.yaml<br>apiVersion: v1<br>kind: PersistentVolumeClaim<br>metadata:<br>&nbsp;&nbsp;name: image-registry<br>&nbsp;&nbsp;namespace: openshift-image-registry<br>spec:<br>&nbsp;&nbsp;accessModes:<br>&nbsp;&nbsp;&#8211; ReadWriteOnce<br>&nbsp;&nbsp;resources:<br>&nbsp;&nbsp;&nbsp;&nbsp;requests:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;storage: 100Gi<br>EOF<br>oc apply -f registry-pvc.yaml<br>oc edit configs.imageregistry.operator.openshift.io<br>[&#8230;]<br>&nbsp;&nbsp;storage:<br>&nbsp;&nbsp;&nbsp;&nbsp;pvc:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;claim: image-registry-storage<br>[&#8230;]<br>oc get pods -n openshift-image-registry -w<\/p>\n<\/blockquote>\n\n\n<p><\/p>\n\n\n<h2 style=\"padding-left: 40px;\"><em><strong>Conclusion<\/strong><\/em><\/h2>\n<p>First thing I would regret is the disappearance of rbd binaries from controllers images. As a result, the Ceph provisioner we used to configure with OpenShift3 no longer works. Apparently, CSI provisioners would be recommended instead, though that implementation is kind of slower, and involves quite a lot of Pods.<\/p>\n<p>After deployment, roughly 12G RAM and 4 CPUs are allocated to cluster operators and OpenShift internals.<\/p>\n<p>Another concern may be that all those operators are privileged actors in our cluster. While we usually had to compromise a node to attack a cluster, now we have a lot of operators that might be accessed through the API, arguably expanding OpenShift attack surface.<\/p>\n<p>The dashboard shows a total CPU capacity of &#8220;100%&#8221;, which is quite useless.<\/p>\n<p>OpenShift 4.2 is based on Kubernetes 1.14. Among nother novelties, as compared with OpenShift 3, we could mention Istio reaching GA or Tekton pipelines.<\/p>","protected":false},"excerpt":{"rendered":"<p>Once again, quick post regarding OpenShift, today experimenting with the new installer, and OpenShift 4. First, let&#8217;s remind ourselves that OKD 4 has not yet been released. I would be using my RedHat account credentials pulling images. I usually refuse to touch anything that is not strictly open source (and freely distributed), though I would [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[12,13,2],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.unetresgrossebite.com\/index.php?rest_route=\/wp\/v2\/posts\/860"}],"collection":[{"href":"https:\/\/blog.unetresgrossebite.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.unetresgrossebite.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.unetresgrossebite.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.unetresgrossebite.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=860"}],"version-history":[{"count":10,"href":"https:\/\/blog.unetresgrossebite.com\/index.php?rest_route=\/wp\/v2\/posts\/860\/revisions"}],"predecessor-version":[{"id":875,"href":"https:\/\/blog.unetresgrossebite.com\/index.php?rest_route=\/wp\/v2\/posts\/860\/revisions\/875"}],"wp:attachment":[{"href":"https:\/\/blog.unetresgrossebite.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=860"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.unetresgrossebite.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=860"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.unetresgrossebite.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=860"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}