diff --git a/polkadot/.gitlab-ci.yml b/polkadot/.gitlab-ci.yml index 77b4603209..31bea94204 100644 --- a/polkadot/.gitlab-ci.yml +++ b/polkadot/.gitlab-ci.yml @@ -1,18 +1,32 @@ +# .gitlab-ci.yml +# +# pipelines can be triggered manually in the web +# setting POLKADOT_BRANCH to v0.2 will result in a poc2 build +# setting DEPLOY_TAG will only deploy the tagged image + + stages: - test - build + - publish + - deploy + + + +# default release in rust:nightly image is stable image: parity/rust:nightly variables: CI_SERVER_NAME: "GitLab CI" CARGO_HOME: "${CI_PROJECT_DIR}/.cargo" + SUBSTRATE_REPO: "https://github.com/paritytech/substrate.git" + cache: key: "${CI_JOB_NAME}" paths: - - ./target/ - ./.cargo/ @@ -22,43 +36,156 @@ cache: when: on_success expire_in: 7 days paths: - - target/release/polkadot - -.determine_version: &determine_version | - export VERSION="$(sed -r -n '1,/^version/s/^version = "([^"]+)".*$/\1/p' Cargo.toml)" - echo "Version" $VERSION + - target/release/ -before_script: - - ./scripts/build.sh + +# disabled as there are bugs +# before_script: +# - ./scripts/build.sh -#### stage: test -test:rust:stable: +# test will be run for ci on the current repo +# for version v0.2 branch tests are located in substrate repository and +# therefore not generically testable +test:rust:release: stage: test - script: - - time cargo test --all --release only: - triggers - tags - master - schedules - web + - /^[0-9]+$/ + except: + variables: + - $POLKADOT_BRANCH == "v0.2" + - $DEPLOY_TAG tags: - - rust-stable - - -#### stage: build - -build:linux:ubuntu:amd64: - stage: build + - rust script: - - cargo build --release + - time cargo test --all --release --verbose + + + +build:rust:linux:release: + stage: build <<: *collect_artifacts only: - master - tags + - web + except: + variables: + - $DEPLOY_TAG tags: - - rust-stable + - rust + script: + - > + set -x; + if [ "${POLKADOT_BRANCH}" = "v0.2" ]; then + if [ -z "${TAG}" ]; then + time cargo install --verbose --root ./target/release/ --git "${SUBSTRATE_REPO}" --branch "${POLKADOT_BRANCH}" polkadot; + else + time cargo install --verbose --root ./target/release/ --git "${SUBSTRATE_REPO}" --tag "${TAG}" polkadot; + fi; + mv ./target/release/bin/polkadot ./target/release/polkadot; + rm -d ./target/release/bin; + else + time cargo build --release --verbose; + fi; + set +x + - ./target/release/polkadot --version + + + +dockerize:release: + stage: publish + dependencies: + - build:rust:linux:release + cache: {} + only: + - master + - tags + - web + except: + variables: + - $DEPLOY_TAG + tags: + - shell + variables: + DOCKERFILE: scripts/docker/Dockerfile + CONTAINER_IMAGE: parity/polkadot + before_script: + - test "$Docker_Hub_User_Parity" -a "$Docker_Hub_Pass_Parity" + || ( echo "no docker credentials provided"; exit 1 ) + - docker login -u "$Docker_Hub_User_Parity" -p "$Docker_Hub_Pass_Parity" + - docker info + - VERSION="$(./target/release/polkadot --version | sed -n -r 's/^polkadot ([0-9.]+-[0-9a-f]+)-.*$/\1/p')" + - export VERSION + - echo "Polkadot version = ${VERSION}" + script: + - test -z "${VERSION}" && exit 1 + - docker build --tag $CONTAINER_IMAGE:$VERSION --tag $CONTAINER_IMAGE:latest -f $DOCKERFILE ./target/release/ + - docker push $CONTAINER_IMAGE:$VERSION + - docker push $CONTAINER_IMAGE:latest + - rm -f ./target/release/polkadot + - echo "${VERSION}" > ./target/release/VERSION + after_script: + - docker logout + # use artifacts here to transport the version to the next stage + <<: *collect_artifacts + + + + + +.deploy:template: &deploy + stage: deploy + when: manual + cache: {} + retry: 1 + image: dtzar/helm-kubectl:$HELM_VERSION + only: + - master + - tags + - web + tags: + - kubernetes + before_script: + - test -z "${DEPLOY_TAG}" && + test -f ./target/release/VERSION && + DEPLOY_TAG="$(cat ./target/release/VERSION)" + - test "${DEPLOY_TAG}" || ( echo "Neither DEPLOY_TAG nor VERSION information available"; exit 1 ) + script: + - echo "Polkadot version = ${DEPLOY_TAG}" + # or use helm to render the template + - helm template + --values ./scripts/kubernetes/values.yaml + --set image.tag=${DEPLOY_TAG} + ./scripts/kubernetes | kubectl apply -f - --dry-run=true + - echo "# polkadot namespace" + - kubectl -n polkadot get all + - echo "# polkadot's nodes' external ip addresses:" + - kubectl get nodes -l node=polkadot + -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{range @.status.addresses[?(@.type=="ExternalIP")]}{.address}{"\n"}{end}' + - echo "# polkadots' nodes" + - kubectl -n polkadot get pods + -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.nodeName}{"\n"}{end}' + + + +# have environment:url eventually point to the logs + +deploy:ew3: + <<: *deploy + environment: + name: gke-beta-ew3 + +deploy:ue1: + <<: *deploy + environment: + name: gke-beta-ue1 + diff --git a/polkadot/scripts/docker/Dockerfile b/polkadot/scripts/docker/Dockerfile new file mode 100644 index 0000000000..8d4e21788f --- /dev/null +++ b/polkadot/scripts/docker/Dockerfile @@ -0,0 +1,29 @@ +FROM ubuntu:xenial +LABEL maintainer "devops-team@parity.io" +LABEL description="Paritytech Polkadot Node Implementation" + +RUN apt-get update && \ + apt-get upgrade -y && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + libssl1.0.0 \ + ca-certificates \ + curl && \ + apt-get autoremove -y && \ + apt-get clean + +RUN find /var/lib/apt/lists/ -type f -not -name lock -delete + +COPY ./polkadot /usr/local/bin + + + +RUN useradd -m -u 1000 -U -s /bin/sh -d /polkadot polkadot +USER polkadot + +ENV RUST_BACKTRACE 1 + +EXPOSE 30333 9933 9944 +VOLUME ["/polkadot"] + +ENTRYPOINT ["/usr/local/bin/polkadot"] + diff --git a/polkadot/scripts/kubernetes/Chart.yaml b/polkadot/scripts/kubernetes/Chart.yaml new file mode 100644 index 0000000000..885cec5799 --- /dev/null +++ b/polkadot/scripts/kubernetes/Chart.yaml @@ -0,0 +1,12 @@ +name: polkadot +version: 0.1 +appVersion: 0.2.0 +description: Polkadot Node Implementation +home: https://polkadot.network/ +icon: https://polkadot.network/favicon.ico +sources: + - https://github.com/paritytech/polkadot/ +maintainers: + - name: Paritytech Devops Team + email: devops-team@parity.io +tillerVersion: ">=2.8.0" diff --git a/polkadot/scripts/kubernetes/README.md b/polkadot/scripts/kubernetes/README.md new file mode 100644 index 0000000000..1ae9ff79c0 --- /dev/null +++ b/polkadot/scripts/kubernetes/README.md @@ -0,0 +1,47 @@ + + +# Polkadot Kubernetes Helm Chart + +This [Helm Chart](https://helm.sh/) can be used for deploying containerized +**Polkadot** to a [Kubernetes](https://kubernetes.io/) cluster. + + +## Prerequisites + +- Tested on Kubernetes 1.10.7-gke.6 + +## Installation + +To install the chart with the release name `my-release` into namespace +`my-namespace` from within this directory: + +```console +$ helm install --namespace my-namespace --name my-release --values values.yaml ./ +``` + +The command deploys Polkadot on the Kubernetes cluster in the configuration +given in `values.yaml`. When the namespace is omitted it'll be installed in +the default one. + + +## Removal of the Chart + +To uninstall/delete the `my-release` deployment: + +```console +$ helm delete --namespace my-namespace my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + + +## Upgrading + +Once the chart is installed and a new version should be deployed helm takes +care of this by + +```console +$ helm upgrade --namespace my-namespace --values values.yaml my-release ./ +``` + + diff --git a/polkadot/scripts/kubernetes/templates/poddisruptionbudget.yaml b/polkadot/scripts/kubernetes/templates/poddisruptionbudget.yaml new file mode 100644 index 0000000000..e19eae5f5d --- /dev/null +++ b/polkadot/scripts/kubernetes/templates/poddisruptionbudget.yaml @@ -0,0 +1,10 @@ +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: polkadot +spec: + selector: + matchLabels: + app: polkadot + maxUnavailable: 1 + diff --git a/polkadot/scripts/kubernetes/templates/service.yaml b/polkadot/scripts/kubernetes/templates/service.yaml new file mode 100644 index 0000000000..f64f4ac1bf --- /dev/null +++ b/polkadot/scripts/kubernetes/templates/service.yaml @@ -0,0 +1,39 @@ +# see: +# https://kubernetes.io/docs/tutorials/services/ +# https://kubernetes.io/docs/concepts/services-networking/service/ +# headless service for rpc +apiVersion: v1 +kind: Service +metadata: + name: polkadot-rpc + labels: + app: polkadot +spec: + ports: + - port: 9933 + name: http-rpc + - port: 9944 + name: websocket-rpc + selector: + app: polkadot + sessionAffinity: None + type: ClusterIP + clusterIP: None +--- +apiVersion: v1 +kind: Service +metadata: + name: polkadot +spec: + ports: + - port: 30333 + name: p2p + nodePort: 30333 + protocol: TCP + selector: + app: polkadot + sessionAffinity: None + type: NodePort + # don't route exteral traffic to non-local pods + externalTrafficPolicy: Local + diff --git a/polkadot/scripts/kubernetes/templates/serviceaccount.yaml b/polkadot/scripts/kubernetes/templates/serviceaccount.yaml new file mode 100644 index 0000000000..207cea964a --- /dev/null +++ b/polkadot/scripts/kubernetes/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.rbac.enable }} +# service account for polkadot pods themselves +# no permissions for the api are required +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app: polkadot + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + release: {{ .Release.Name }} + name: {{ .Values.rbac.name }} +{{- end }} diff --git a/polkadot/scripts/kubernetes/templates/statefulset.yaml b/polkadot/scripts/kubernetes/templates/statefulset.yaml new file mode 100644 index 0000000000..cb741d7c9d --- /dev/null +++ b/polkadot/scripts/kubernetes/templates/statefulset.yaml @@ -0,0 +1,102 @@ +# https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/ +# https://cloud.google.com/kubernetes-engine/docs/concepts/statefulset +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: polkadot +spec: + selector: + matchLabels: + app: polkadot + serviceName: polkadot + replicas: {{ .Values.nodes.replicas }} + updateStrategy: + type: RollingUpdate + podManagementPolicy: Parallel + template: + metadata: + labels: + app: polkadot + spec: + {{- if .Values.rbac.enable }} + serviceAccountName: {{ .Values.rbac.name }} + {{- else }} + serviceAccountName: default + {{- end }} + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node + operator: In + values: + - polkadot + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: "app" + operator: In + values: + - polkadot + topologyKey: "kubernetes.io/hostname" + terminationGracePeriodSeconds: 300 + containers: + - name: polkapod + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + {{- if .Values.resources }} + resources: + requests: + memory: {{ .Values.resources.memory }} + cpu: {{ .Values.resources.cpu }} + {{- end }} + ports: + - containerPort: 30333 + name: p2p + - containerPort: 9933 + name: http-rpc + - containerPort: 9944 + name: websocket-rpc + args: + - --base-path + - {{ .Values.image.basepath }} + - --name + - $(MY_POD_NAME) + {{- range .Values.nodes.args }} + - {{ . }} + {{- end }} + env: + # from (workaround for hostname) + # https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/ + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + volumeMounts: + - name: polkadir + mountPath: {{ .Values.image.basepath }} + readinessProbe: + tcpSocket: + port: http-rpc + initialDelaySeconds: 30 + periodSeconds: 30 + livenessProbe: + tcpSocket: + port: http-rpc + initialDelaySeconds: 30 + periodSeconds: 30 + securityContext: + runAsUser: 1000 + fsGroup: 1000 + volumeClaimTemplates: + - metadata: + name: polkadir + spec: + accessModes: [ "ReadWriteOnce" ] + storageClassName: ssd + resources: + requests: + storage: 32Gi + diff --git a/polkadot/scripts/kubernetes/values.yaml b/polkadot/scripts/kubernetes/values.yaml new file mode 100644 index 0000000000..b32e6126b7 --- /dev/null +++ b/polkadot/scripts/kubernetes/values.yaml @@ -0,0 +1,37 @@ +# set tag manually --set image.tag=latest +image: + repository: parity/polkadot + tag: latest + pullPolicy: Always + basepath: /polkadot + + +# if set to true a service account for polkadot will be created +rbac: + enable: true + name: polkadot + + +nodes: + replicas: 2 + args: + - --chain + - krummelanke + # serve rpc within the local network + # - fenced off the world via firewall + # - used for health checks + - --rpc-external + - --ws-external + # - --log + # - sub-libp2p=trace + # - --validator + # - --key + # - key_name + + + +# maybe adopt resource limits here to the nodes of the pool +# resources: +# memory: "5Gi" +# cpu: "1.5" +