# A headless service to create DNS records. apiVersion: v1 kind: Service metadata: name: {{ template "hdfs-k8s.namenode.fullname" . }} labels: app: {{ template "hdfs-k8s.namenode.name" . }} chart: {{ template "hdfs-k8s.subchart" . }} release: {{ .Release.Name }} annotations: # TODO: Deprecated. Replace tolerate-unready-endpoints with # v1.Service.PublishNotReadyAddresses. service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" spec: ports: - port: 8020 name: fs - port: 50070 name: http clusterIP: None selector: app: {{ template "hdfs-k8s.namenode.name" . }} release: {{ .Release.Name }} --- apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: name: {{ template "hdfs-k8s.namenode.fullname" . }} labels: app: {{ template "hdfs-k8s.namenode.name" . }} chart: {{ template "hdfs-k8s.subchart" . }} release: {{ .Release.Name }} spec: selector: matchLabels: app: {{ template "hdfs-k8s.namenode.name" . }} release: {{ .Release.Name }} minAvailable: 1 --- # Provides namenode helper scripts. Most of them are start scripts # that meet different needs. # TODO: Support upgrade of metadata in case a new Hadoop version requires it. apiVersion: v1 kind: ConfigMap metadata: name: {{ template "hdfs-k8s.namenode.fullname" . }}-scripts labels: app: {{ template "hdfs-k8s.namenode.name" . }} chart: {{ template "hdfs-k8s.subchart" . }} release: {{ .Release.Name }} data: # A bootstrap script which will start namenode daemons after conducting # optional metadata initialization steps. The metadata initialization # steps will take place in case the metadata dir is empty, # which will be the case only for the very first run. The specific steps # will differ depending on whether the namenode is active or standby. # We also assume, for the very first run, namenode-0 will be active and # namenode-1 will be standby as StatefulSet will launch namenode-0 first # and zookeeper will determine the sole namenode to be the active one. # For active namenode, the initialization steps will format the metadata, # zookeeper dir and journal node data entries. # For standby namenode, the initialization steps will simply receieve # the first batch of metadata updates from the journal node. format-and-run.sh: | #!/usr/bin/env bash # Exit on error. Append "|| true" if you expect an error. set -o errexit # Exit on error inside any functions or subshells. set -o errtrace # Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR set -o nounset # Catch an error in command pipes. e.g. mysqldump fails (but gzip succeeds) # in `mysqldump |gzip` set -o pipefail # Turn on traces, useful while debugging. set -o xtrace _HDFS_BIN=$HADOOP_PREFIX/bin/hdfs _METADATA_DIR=/hadoop/dfs/name/current if [[ "$MY_POD" = "$NAMENODE_POD_0" ]]; then if [[ ! -d $_METADATA_DIR ]]; then $_HDFS_BIN --config $HADOOP_CONF_DIR namenode -format \ -nonInteractive hdfs-k8s || (rm -rf $_METADATA_DIR; exit 1) fi _ZKFC_FORMATTED=/hadoop/dfs/name/current/.hdfs-k8s-zkfc-formatted if [[ ! -f $_ZKFC_FORMATTED ]]; then _OUT=$($_HDFS_BIN --config $HADOOP_CONF_DIR zkfc -formatZK -nonInteractive 2>&1) # zkfc masks fatal exceptions and returns exit code 0 (echo $_OUT | grep -q "FATAL") && exit 1 touch $_ZKFC_FORMATTED fi elif [[ "$MY_POD" = "$NAMENODE_POD_1" ]]; then if [[ ! -d $_METADATA_DIR ]]; then $_HDFS_BIN --config $HADOOP_CONF_DIR namenode -bootstrapStandby \ -nonInteractive || \ (rm -rf $_METADATA_DIR; exit 1) fi fi $HADOOP_PREFIX/sbin/hadoop-daemon.sh --config $HADOOP_CONF_DIR start zkfc $_HDFS_BIN --config $HADOOP_CONF_DIR namenode # A start script that will just hang indefinitely. A user can then get # inside the pod and debug. Or a user can conduct a custom manual operations. do-nothing.sh: | #!/usr/bin/env bash tail -f /var/log/dmesg # A start script that has user specified content. Can be used to conduct # ad-hoc operation as specified by a user. custom-run.sh: {{ .Values.customRunScript | quote }} --- apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: {{ template "hdfs-k8s.namenode.fullname" . }} labels: app: {{ template "hdfs-k8s.namenode.name" . }} chart: {{ template "hdfs-k8s.subchart" . }} release: {{ .Release.Name }} spec: serviceName: {{ template "hdfs-k8s.namenode.fullname" . }} replicas: 2 template: metadata: labels: app: {{ template "hdfs-k8s.namenode.name" . }} release: {{ .Release.Name }} {{- if .Values.podAnnotations }} annotations: {{ toYaml .Values.podAnnotations | indent 8 }} {{- end }} spec: {{- if .Values.hostNetworkEnabled }} # Use hostNetwork so datanodes connect to namenode without going through an overlay network # like weave. Otherwise, namenode fails to see physical IP address of datanodes. # Disabling this will break data locality as namenode will see pod virtual IPs and fails to # equate them with cluster node physical IPs associated with data nodes. # We currently disable this only for CI on minikube. hostNetwork: true hostPID: true dnsPolicy: ClusterFirstWithHostNet {{- else }} dnsPolicy: ClusterFirst {{- end }} {{- if .Values.affinity }} affinity: {{ toYaml .Values.affinity | indent 8 }} {{- else if .Values.global.defaultAffinityEnabled }} affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: "app" operator: In values: - {{ template "hdfs-k8s.namenode.name" . }} - key: "release" operator: In values: - {{ .Release.Name }} topologyKey: "kubernetes.io/hostname" {{- end }} {{- if .Values.nodeSelector }} nodeSelector: {{ toYaml .Values.nodeSelector | indent 8 }} {{- end }} {{- if .Values.tolerations }} tolerations: {{ toYaml .Values.tolerations | indent 8 }} {{- end }} containers: # TODO: Support hadoop version as option. - name: hdfs-namenode image: uhopper/hadoop-namenode:2.7.2 env: - name: HADOOP_CUSTOM_CONF_DIR value: /etc/hadoop-custom-conf - name: MULTIHOMED_NETWORK value: "0" # Used by the start script below. - name: MY_POD valueFrom: fieldRef: fieldPath: metadata.name - name: NAMENODE_POD_0 value: {{ template "namenode-pod-0" . }} - name: NAMENODE_POD_1 value: {{ template "namenode-pod-1" . }} command: ['/bin/sh', '-c'] # The start script is provided by a config map. args: - /entrypoint.sh "/nn-scripts/{{ .Values.namenodeStartScript }}" ports: - containerPort: 8020 name: fs - containerPort: 50070 name: http volumeMounts: - name: nn-scripts mountPath: /nn-scripts readOnly: true # Mount a subpath of the volume so that the name subdir would be a # brand new empty dir. This way, we won't get affected by existing # files in the volume top dir. - name: metadatadir mountPath: /hadoop/dfs/name subPath: name - name: hdfs-config mountPath: /etc/hadoop-custom-conf readOnly: true {{- if .Values.global.kerberosEnabled }} - name: kerberos-config mountPath: /etc/krb5.conf subPath: {{ .Values.global.kerberosConfigFileName }} readOnly: true - name: kerberos-keytab-copy mountPath: /etc/security/ readOnly: true {{- end }} {{- if .Values.global.kerberosEnabled }} initContainers: - name: copy-kerberos-keytab image: busybox:1.27.1 command: ['sh', '-c'] args: - cp /kerberos-keytabs/${MY_KERBEROS_NAME}*.keytab /kerberos-keytab-copy/hdfs.keytab env: - name: MY_KERBEROS_NAME valueFrom: fieldRef: {{- if .Values.hostNetworkEnabled }} fieldPath: spec.nodeName {{- else }} fieldPath: metadata.name {{- end }} volumeMounts: - name: kerberos-keytabs mountPath: /kerberos-keytabs - name: kerberos-keytab-copy mountPath: /kerberos-keytab-copy {{- end }} restartPolicy: Always volumes: - name: nn-scripts configMap: name: {{ template "hdfs-k8s.namenode.fullname" . }}-scripts defaultMode: 0744 - name: hdfs-config configMap: name: {{ template "hdfs-k8s.config.fullname" . }} {{- if .Values.global.kerberosEnabled }} - name: kerberos-config configMap: name: {{ template "krb5-configmap" . }} - name: kerberos-keytabs secret: secretName: {{ template "krb5-keytabs-secret" . }} - name: kerberos-keytab-copy emptyDir: {} {{- end }} {{- if .Values.global.podSecurityContext.enabled }} securityContext: runAsUser: {{ .Values.global.podSecurityContext.runAsUser }} fsGroup: {{ .Values.global.podSecurityContext.fsGroup }} {{- end }} volumeClaimTemplates: - metadata: name: metadatadir spec: accessModes: - {{ .Values.persistence.accessMode | quote }} resources: requests: storage: {{ .Values.persistence.size | quote }} {{- if .Values.persistence.storageClass }} {{- if (eq "-" .Values.persistence.storageClass) }} storageClassName: "" {{- else }} storageClassName: "{{ .Values.persistence.storageClass }}" {{- end }} {{- end }} {{- if .Values.persistence.selector }} selector: {{ toYaml .Values.persistence.selector | indent 10 }} {{- end }}