diff --git a/.github/workflows/helm_deploy.yml b/.github/workflows/helm_deploy.yml new file mode 100644 index 0000000..d79342c --- /dev/null +++ b/.github/workflows/helm_deploy.yml @@ -0,0 +1,41 @@ +name: Build and publish a beamline's ioc helm charts + +on: + push: + pull_request: + +env: + HELM_VERSION_TO_INSTALL: 3.8.2 # version of HELM to install + +jobs: + build-and-push-helm-charts: + name: publish helm charts to ghcr.io + runs-on: ubuntu-latest + environment: prod + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: install helm + uses: Azure/setup-helm@v1 + with: + version: ${{ env.HELM_VERSION_TO_INSTALL }} + + - name: push the helm chart + run: | + echo ${{ secrets.GITHUB_TOKEN }} | helm registry login ghcr.io/${{ github.repository_owner }} --username ${{ github.repository_owner }} --password-stdin + + REGISTRY=oci://ghcr.io/epics-containers + + # helm tags must be SemVar. Use 0.0.0-b0 for testing the latest non-tagged build + if [ "${GITHUB_REF_TYPE}" == "tag" ] ; then + TAG=${GITHUB_REF_NAME} + else + TAG="0.0.0-b0" + fi + NAME=$(sed -n '/^name: */s///p' Chart.yaml) + + helm package -u --app-version ${TAG} --version ${TAG} . + PACKAGE=${NAME}-${TAG}.tgz + + helm push "$PACKAGE" $REGISTRY diff --git a/Charts/beamline-chart/.helmignore b/Charts/beamline-chart/.helmignore new file mode 100644 index 0000000..7c54e53 --- /dev/null +++ b/Charts/beamline-chart/.helmignore @@ -0,0 +1,28 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ + +# from this project +ghcr-deploy.sh +*.tgz +.github diff --git a/Charts/beamline-chart/Chart.yaml b/Charts/beamline-chart/Chart.yaml new file mode 100644 index 0000000..9ef5774 --- /dev/null +++ b/Charts/beamline-chart/Chart.yaml @@ -0,0 +1,6 @@ +# A Helm Chart for an IOC instance +apiVersion: v2 +name: ioc-instance + +# this is replaced by command line arguments when `ec` invokes helm +version: 0.0.1 diff --git a/Charts/beamline-chart/README.md b/Charts/beamline-chart/README.md new file mode 100644 index 0000000..0d69e9d --- /dev/null +++ b/Charts/beamline-chart/README.md @@ -0,0 +1,26 @@ +Beamline or Accelerator Domain Helm Chart Template +================================================== + +The files in this folder are used to generate a helm chart for IOC instances +on this repository's beamline or accelerator domain. + +We generate a new helm chart for each IOC instance deployment. This is +primarily because helm cannot package the config folder at package time. +This approach could therefore be reviewed once this PR is released: +https://github.com/helm/helm/pull/10077 + +The other reasons for this approach are: + +1. We prefer to have the entire definition of an IOC domain in a single repository. + rather than pulling the chart from a chart repository. + +To deploy at IOC using beamline chart make sure you have epics-containers-cli +installed and you are connected to the correct kubernetes cluster. + +```bash + source environment.sh + ec ioc deploy +``` + +see: [EPICS Containers CLI Repository](https://github.com/epics-containers/epics-containers-cli). + diff --git a/Charts/beamline-chart/templates/deployment.yaml b/Charts/beamline-chart/templates/deployment.yaml new file mode 100644 index 0000000..d7b49ed --- /dev/null +++ b/Charts/beamline-chart/templates/deployment.yaml @@ -0,0 +1,140 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.ioc_name }} + labels: + app: {{ .Values.ioc_name }} + beamline: {{ .Values.beamline }} + ioc_version: {{ .Values.ioc_version | quote }} + is_ioc: "True" + annotations: + kubernetes.io/change-cause: {{ .Values.ioc_version }} deployed +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Values.ioc_name }} + template: + metadata: + labels: + app: {{ .Values.ioc_name }} + beamline: {{ .Values.beamline }} + ioc_version: {{ .Values.ioc_version | quote }} + is_ioc: "True" + spec: + {{ if .Values.serviceAccountName }} + serviceAccountName: {{ .Values.serviceAccountName | quote }} + {{- end }} + hostNetwork: {{ .Values.hostNetwork }} + terminationGracePeriodSeconds: 15 # nice to have quick restarts on IOCs + volumes: + {{- if .Values.opisClaim }} + - name: opis-volume + persistentVolumeClaim: + claimName: {{ .Values.opisClaim }} + {{- end }} + {{- if .Values.nfsv2TftpClaim }} + - name: nfsv2-tftp-volume + persistentVolumeClaim: + claimName: {{ .Values.nfsv2TftpClaim }} + {{- end }} + {{- if .Values.autosave }} + - name: {{ .Values.ioc_name }} + persistentVolumeClaim: + claimName: {{ .Values.ioc_name }} + {{- end }} + {{- if .Values.dataVolume.pvc }} + - name: {{ .Values.ioc_name }}-data + persistentVolumeClaim: + claimName: {{ .Values.ioc_name }}-data + {{- else if .Values.dataVolume.hostPath }} + - name: {{ .Values.ioc_name }}-data + hostPath: + path: {{ .Values.dataVolume.hostPath }} + type: Directory + {{- end }} + - name: config-volume + configMap: + name: {{ .Values.ioc_name }} + containers: + - name: {{ .Values.ioc_name }} + image: {{ .Values.image }} + command: + - bash + args: + - {{ .Values.start }} + livenessProbe: + exec: + command: + - /bin/bash + - {{ .Values.liveness }} + initialDelaySeconds: 120 + periodSeconds: 10 + lifecycle: + preStop: + exec: + command: ["bash", "-c", "{{ .Values.stop }}"] + volumeMounts: + - name: config-volume + mountPath: {{ .Values.iocConfig }} + {{- if .Values.autosave }} + - name: {{ .Values.ioc_name }} + mountPath: /autosave + {{- end }} + {{- if or (.Values.dataVolume.pvc) (.Values.dataVolume.hostPath) }} + - name: {{ .Values.ioc_name }}-data + mountPath: {{ .Values.dataVolume.hostPath }} + {{- if .Values.dataVolume.hostPath }} + mountPropagation: HostToContainer + {{- end}} + {{- end }} + {{- if .Values.nfsv2TftpClaim }} + - name: nfsv2-tftp-volume + mountPath: /nfsv2-tftp + subPath: "{{ .Values.beamline }}/{{ .Values.ioc_name }}" + {{- end }} + {{- if .Values.opisClaim }} + - name: opis-volume + mountPath: /epics/opis + subPath: "{{ .Values.ioc_name }}" + {{- end }} + stdin: true + tty: true + securityContext: +{{ toYaml .Values.securityContext | indent 10}} + resources: +{{ toYaml .Values.resources | indent 10}} + imagePullPolicy: Always + env: + - name: IOCSH_PS1 + value: "{{ .Values.ioc_name }} > " + - name: IOC_NAME + value: {{ .Values.ioc_name }} + - name: IOC_PREFIX + value: {{ or .Values.prefix .Values.ioc_name | quote }} + - name: IOC_VERSION + value: {{ .Values.ioc_version | quote }} + {{- range $envVar := .Values.env }} + - name: {{ $envVar.name }} + value: {{ $envVar.value | quote }} + {{- end }} + {{- range $envVar := .Values.globalenv }} + - name: {{ $envVar.name }} + value: {{ $envVar.value | quote }} + {{- end }} + {{- if .Values.useAffinity }} + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: beamline + operator: In + values: + - {{ .Values.beamline }} + {{- end }} + tolerations: + - key: nodetype + operator: Equal + value: {{ .Values.beamline }} + effect: NoSchedule diff --git a/Charts/beamline-chart/templates/ioc-volume.yaml b/Charts/beamline-chart/templates/ioc-volume.yaml new file mode 100644 index 0000000..b566a9d --- /dev/null +++ b/Charts/beamline-chart/templates/ioc-volume.yaml @@ -0,0 +1,17 @@ +# IOCS can use a PVC as a data volume, or mount the host filesystem +{{- if .Values.dataVolume.pvc }} +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ .Values.beamline }}-data + labels: + app: {{ .Values.beamline }}-data + beamline: {{ .Values.beamline }} +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 1000Mi +{{- end }} diff --git a/Charts/beamline-chart/templates/ioc.yaml b/Charts/beamline-chart/templates/ioc.yaml new file mode 100644 index 0000000..da317fc --- /dev/null +++ b/Charts/beamline-chart/templates/ioc.yaml @@ -0,0 +1,20 @@ +{{- /* + add the contents of every file in the config folder of this IOC helm chart + into the config map - The files must be text only. +*/ -}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.ioc_name }} + labels: + app: {{ .Values.ioc_name }} + beamline: {{ .Values.beamline }} + ioc_version: {{ .Chart.AppVersion | quote }} + is_ioc: "True" +data: + +{{ (.Files.Glob "config/*").AsConfig | indent 2 }} + version.txt: | + IOC {{ .Release.Name }} version {{ .Chart.AppVersion }} + diff --git a/Charts/beamline-chart/values.yaml b/Charts/beamline-chart/values.yaml new file mode 100644 index 0000000..ac43855 --- /dev/null +++ b/Charts/beamline-chart/values.yaml @@ -0,0 +1,83 @@ +# default values for all IOCs on bl38p +# see end of file for values that may be overridden per individual IOC + +beamline: bl38p +# we use the default service account in the namespace so leave this out +# if you need to specify an alternative then uncomment below +# serviceAccountName: k8s-p38-iocs + +# useHostNetwork - use host network for IOC - required for Channel Access +# to work outside of the cluster +hostNetwork: true + +# useAffinity - only run on nodes with label beamline: +# Not using this now that we have cluster per beamline +# However if you are using one cluster for multiple beamlines / domains +# Then label the local nodes for each beamline and switch to true +useAffinity: false + +# root folder for ioc source/binaries inside generic IOC container +iocFolder: /epics/ioc +iocConfig: /epics/ioc/config +# scripts for controlling the IOC +start: /epics/ioc/start.sh +stop: /epics/ioc/stop.sh +liveness: /epics/ioc/liveness.sh + +# the following are added to the pod's environment +globalenv: + # Where to find RTEMS IOC files + - name: "K8S_IOC_TFTP_ADDR" + value: "172.23.168.220" + - name: "K8S_IOC_TFTP_PORT" + value: "69" + - name: "K8S_IOC_NFS_MOUNT" + value: "172.23.168.220:iocs" + +# reasonable defaults for securityContext +securityContext: + allowPrivilegeEscalation: false + # IMPORTANT if you want access to /dls/p38/data then the following should + # be set to p38detector account ID and group ID + # runAsUser: 37143 + # runAsGroup: 37143 + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + +######################################################################################## +# The following values are expected to be overridden in individual IOC values.yaml +######################################################################################## + +# these values are the only mandatory overrides. We supply image in the values.yaml +# for each IOC instance. `ec ioc deploy` supplies ioc_name and ioc_version +# on the command line to helm. +image: +ioc_name: +ioc_version: + +# a volume to mount for writing data to. This can be a PVC or a hostPath +# the PVC +dataVolume: + # Create a PVC called {{ .Chart.Name }}-data when true + pvc: false + # A path on the host machine to write data into. Also used as the path that the + # pvc or hostPath will be mounted at so that users need not be confused about + # inside/outside container paths. + hostPath: /dls/p38/data/ + +# use the shared PVC for publishing opi files over http (see services/opis) +opisClaim: bl38p-opi-claim +# use the shared PVC autosave files (comment out for no autosave) +autosaveClaim: bl38p-autosave-claim + +# default resource limits +resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 100m + memory: 64Mi diff --git a/Charts/opis/Chart.yaml b/Charts/opis/Chart.yaml new file mode 100644 index 0000000..6a46ae7 --- /dev/null +++ b/Charts/opis/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: domain-opis +description: | + A service for sharing bob files to phoebus for all iocs on the beamline. + +type: application + +version: 0.1.0 diff --git a/Charts/opis/config/nginx.conf b/Charts/opis/config/nginx.conf new file mode 100644 index 0000000..7c2dc30 --- /dev/null +++ b/Charts/opis/config/nginx.conf @@ -0,0 +1,40 @@ +# NOTE: this config is designed to work on an unprivileged pod. + +worker_processes auto; + +# using tmp for logs due to capability DROPS on ioc namespaces +error_log /tmp/error.log notice; +pid /tmp/nginx.pid; + + +events { + # using a low number here as this server won't get much traffic + worker_connections 50; +} + + +http { + autoindex on; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /tmp/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + include /etc/nginx/conf.d/*.conf; + + server { + autoindex on; + } +} diff --git a/Charts/opis/templates/deploy.yaml b/Charts/opis/templates/deploy.yaml new file mode 100644 index 0000000..8a0272d --- /dev/null +++ b/Charts/opis/templates/deploy.yaml @@ -0,0 +1,75 @@ +# a config map to contain nginx config - ingests the files in config folder +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.beamline }}-nginx-config + labels: + app: {{ .Values.beamline }}-opis + beamline: {{ .Values.beamline }} +data: +{{ (.Files.Glob "config/*").AsConfig | indent 2 }} + version.txt: | + IOC {{ .Release.Name }} version {{ .Chart.AppVersion }} +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: {{ .Values.beamline }}-opis + labels: + app: {{ .Values.beamline }}-opis +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Values.beamline }}-opis + template: + metadata: + labels: + app: {{ .Values.beamline }}-opis + spec: + volumes: + - name: html-volume + persistentVolumeClaim: + claimName: {{ .Values.beamline }}-opi-claim + - name: config-volume + configMap: + name: {{ .Values.beamline }}-nginx-config + containers: + - name: server + image: {{ .Values.image }} + resources: + requests: + memory: 100Mi + cpu: 200m + limits: + memory: 500Mi + cpu: 600m + ports: + - containerPort: 90 + protocol: TCP + volumeMounts: + # - mountPath: /opis + - mountPath: /usr/share/nginx/html + name: html-volume + # mount a config file over the default nginx config + - mountPath: /etc/nginx/nginx.conf + name: config-volume + subPath: nginx.conf +--- +kind: Service +apiVersion: v1 +metadata: + name: {{ .Values.beamline }}-opis + labels: + app: {{ .Values.beamline }}-opis +spec: + type: LoadBalancer + externalTrafficPolicy: Local + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 8080 + sessionAffinity: None + selector: + app: {{ .Values.beamline }}-opis diff --git a/Charts/opis/values.yaml b/Charts/opis/values.yaml new file mode 100644 index 0000000..28dbeb3 --- /dev/null +++ b/Charts/opis/values.yaml @@ -0,0 +1,4 @@ +# use nginx for publishing opi files over http +image: docker.io/nginxinc/nginx-unprivileged:stable-bullseye-perl +beamline: bl38p + diff --git a/Charts/shared/Chart.yaml b/Charts/shared/Chart.yaml new file mode 100644 index 0000000..4ecc69d --- /dev/null +++ b/Charts/shared/Chart.yaml @@ -0,0 +1,12 @@ +# this chart should be installed once per beamline and be the first +# chart installed as it creates resources share by all iocs and other +# EPICS services + +apiVersion: v2 +name: domain-shared-pvcs +description: | + A set of PVCs to share between pods for a beamline/accelerator domain + +type: application + +version: 0.1.0 diff --git a/Charts/shared/templates/deploy.yaml b/Charts/shared/templates/deploy.yaml new file mode 100644 index 0000000..0598290 --- /dev/null +++ b/Charts/shared/templates/deploy.yaml @@ -0,0 +1,29 @@ +# PVC for shared OPI files +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ .Values.beamline }}-opi-claim + labels: + app: {{ .Values.beamline }}-opi + beamline: {{ .Values.beamline }} +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 100Mi +--- +# PVC for Autosave volumes +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ .Values.beamline }}-autosave-claim + labels: + app: {{ .Values.beamline }}-autosave + beamline: {{ .Values.beamline }} +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 1Gi diff --git a/Charts/shared/values.yaml b/Charts/shared/values.yaml new file mode 100644 index 0000000..bc793e5 --- /dev/null +++ b/Charts/shared/values.yaml @@ -0,0 +1,2 @@ +beamline: bl38p +