Transformer course business dialogue robot rasa 3 X continuous integration and continuous deployment
Continuous Integration and Continuous Deployment
Even if the development context assistant is different from developing traditional software, you should still follow software development best practices. Setting up continuous integration and continuous deployment pipelines ensures that incremental updates to the bot can improve it without compromising it.
Overview
Continuous integration is the practice of merging code changes frequently and automatically testing changes when they are submitted. Continuous deployment means that integrated changes are automatically deployed to staging or production environments. They enable you to improve assistants more frequently and effectively test and deploy these changes.
This guide will cover what should be included in the continuous integration and continuous deployment pipeline specific to the Rasa project. It is recommended that you choose a tool that integrates with any Git repository you use.
Continuous Integration
The best way to improve the assistant is to make frequent incremental updates. No matter how small the change is, make sure it doesn't create new problems or negatively affect the assistant's performance.
It is usually best to run checks when merging / pulling requests or submitting. Most tests are fast enough to run with each change. However, you can choose to run resource intensive tests only if some files have changed or other indicators exist. For example, if your code is hosted on Github, you can run the test only if the pull request has a specific tag (such as "NLU test required").
Continuous Integration Pipeline Overview
The continuous integration pipeline should include model training and testing as steps to simplify the deployment process. The first step after saving the new training data is to start the pipeline. This can be started manually or when a request is created or updated.
Next, you need to run various test sets to see the impact of the changes. This includes running tests for data validation, NLU cross validation, and story testing. For more information about testing, see test assistant.
The final step is to check the test results and, if the test is successful, push the change. Once the new model has been trained and tested, it can be deployed automatically using the continuous deployment pipeline.
GitHub Actions CI Pipeline
You can automate data validation, training, and testing using the Rasa Train Test Github operation in the continuous integration pipeline.
An example of a continuous integration pipeline using Github operations is as follows:
jobs: training-testing: name: Training and Testing runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Rasa Train and Test GitHub Action uses: RasaHQ/rasa-train-test-gha@main with: requirements_file: requirements.txt data_validate: true rasa_train: true cross_validation: true rasa_test: true test_type: all publish_summary: true github_token: ${{ secrets.GITHUB_TOKEN }} - name: Upload model if: github.ref == 'refs/heads/main' uses: actions/upload-artifact@master with: name: model path: models
In this pipeline, the Rasa Train Test Github operation performs data verification, model training and story testing in the first step, and uploads the model file in the second step.
A complete list of configurable parameters for the Rasa Train Test Github operation is provided in the readme file of the repository.
When publish_ When summary is set to true, this operation will automatically publish the test results of the model as comments to the associated Pull request:
Pull requests can be approved or rejected based on the evaluation results, and in many cases, if all checks pass, you will want to automate the deployment of the model. You can continue to the next section to learn more about continuous deployment.
Continuous Deployment
To provide improvements to users on a regular basis, you need to automate the deployment process as much as possible.
Once the check is successful, the continuous deployment step is usually run when pushing or merging to a branch.
Deploying Your Rasa Model
If you run a test story in the continuous integration pipeline, you already have a trained model. If the continuous integration results are satisfactory, you can set the continuous deployment pipeline and upload the trained model to the Rasa server. For example, to upload a model to Rasa X:
curl -k -F "model=@models/my_model.tar.gz" "https://example.rasa.com/api/projects/default/models?api_token={your_api_token}"
If you are using Rasa X, you can also mark the uploaded model as production (or any deployment to mark when using multiple deployment environments):
curl -X PUT "https://example.rasa.com/api/projects/default/models/my_model/tags/production"
Update of the code of conduct
If the update includes changes to the model and operation code, and these changes are interdependent in any way, the model should not be automatically marked as production. You first need to build and deploy the updated operation server so that the new model will not invoke operations that did not exist in the operation server before the update.
Deploying Your Action Server
For each update of the operation code, you can automatically build a new image for the operation server and upload it to the image repository. As mentioned above, if the operation server is not compatible with the current production model, be careful to automatically deploy new image tags in production.
As an example, see the Rara Rasa assistant and the Carbon Bot. Both use Github as a continuous integration and continuous deployment tool.
These examples are just two of many possibilities. If you have your favorite continuous integration and continuous deployment settings, please share them with the Rasa community on the forum.
https://github.com/RasaHQ/carbon-bot/blob/master/.github/workflows/model_ci.yml
name: Model CI on: push: branches: - 'master' pull_request: types: [opened, synchronize, reopened] env: DOMAIN: 'carbon.rasa.com' GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_NAME: carbon-assistant NAMESPACE: carbon-assistant RASA_X_IMAGE_NAME: ${{ secrets.RASA_X_IMAGE_NAME }} RASA_X_USERNAME: admin RASA_X_PASSWORD: ${{ secrets.RASA_X_ADMIN_PASSWORD }} jobs: build-model: name: Build, test, and upload model runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.7 uses: actions/setup-python@v1 with: python-version: 3.7 - name: Install dependencies run: | python -m pip install --upgrade "pip<20" pip install -r requirements.txt - name: Check stories are consistent run: | rasa data validate stories --max-history 5 --fail-on-warning - name: Train model run: | rasa train - name: Run Through Test Stories run: | rasa test core --stories test_stories/stories.yml --fail-on-prediction-errors - name: Cross-validate NLU model id: cvnlu if: github.event_name == 'pull_request' run: | rasa test nlu -f 2 --cross-validation python format_results.py - name: Upload Cross Validation Results if: github.event_name == 'pull_request' uses: actions/upload-artifact@v2 with: name: cross-validation-result path: results.md - name: Upload model if: github.event_name == 'push' && (startsWith(github.event.ref, 'refs/tags') || github.ref == 'refs/heads/master') uses: actions/upload-artifact@v2 with: name: model path: models - name: Post cross-val results to PR if: steps.cvnlu.outcome == 'success' uses: samsucik/comment-on-pr@comment-file-contents continue-on-error: true with: msg: results.md build-images: name: Build and Push Images runs-on: ubuntu-latest env: IS_PUSH_EVENT: ${{ github.event_name == 'push' }} steps: - uses: actions/checkout@v2 - name: Set image tag run: | if [[ $IS_PUSH_EVENT == "false" ]] then IMAGE_TAG=${{ github.head_ref }} else IMAGE_TAG=$(basename ${{ github.ref }}) fi echo "IMAGE_TAG=${IMAGE_TAG}-${{ github.sha }}" >> $GITHUB_ENV # Setup gcloud CLI - uses: google-github-actions/setup-gcloud@v0.2.1 name: Google Auth with: service_account_key: ${{ secrets.GKE_SA_KEY }} project_id: ${{ secrets.GKE_PROJECT }} # Configure docker to use the gcloud command-line tool as a credential helper - run: |- gcloud --quiet auth configure-docker - name: Build and push the Docker image run: | # Read and export variables from .env file set -o allexport; source .env; set +o allexport docker build . \ --build-arg RASA_SDK_IMAGE=rasa/rasa-sdk:${RASA_SDK_VERSION} \ --tag gcr.io/${{ secrets.GKE_PROJECT }}/carbon-bot-actions:$IMAGE_TAG docker push gcr.io/${{ secrets.GKE_PROJECT }}/carbon-bot-actions:$IMAGE_TAG deploy-to-cluster: name: Re-deploy the cluster with the latest action server runs-on: ubuntu-latest needs: - build-images if: github.event_name == 'push' && (startsWith(github.event.ref, 'refs/tags') || github.ref == 'refs/heads/master') steps: # Checkout repository because we need the content of the `.env` file later - uses: actions/checkout@v2 - name: Set image tag env: IS_PUSH_EVENT: ${{ github.event_name == 'push' }} run: | if [[ $IS_PUSH_EVENT == "false" ]] then IMAGE_TAG=${{ github.head_ref }} else IMAGE_TAG=$(basename ${{ github.ref }}) fi echo "IMAGE_TAG=${IMAGE_TAG}-${{ github.sha }}" >> $GITHUB_ENV # Setup gcloud CLI - uses: google-github-actions/setup-gcloud@v0.2.1 name: Google Auth with: service_account_key: ${{ secrets.GKE_SA_KEY }} project_id: ${{ secrets.GKE_PROJECT }} # Configure docker to use the gcloud command-line tool as a credential helper - run: |- gcloud --quiet auth configure-docker # Get the GKE credentials so we can deploy to the cluster - uses: google-github-actions/get-gke-credentials@v0.2.1 with: cluster_name: ${{ secrets.GKE_CLUSTER }} location: ${{ secrets.GKE_ZONE }} credentials: ${{ secrets.GKE_SA_KEY }} - name: Install Helm and helmfile ⛑ run: | curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 chmod 700 get_helm.sh ./get_helm.sh sudo curl -fsSL https://github.com/roboll/helmfile/releases/download/v0.138.7/helmfile_linux_amd64 --output /usr/local/bin/helmfile sudo chmod +x /usr/local/bin/helmfile - name: Install Chart env: DB_PASSWORD: ${{ secrets.DB_PASSWORD }} RASA_TOKEN: ${{ secrets.RASA_TOKEN }} RASA_X_TOKEN: ${{ secrets.RASA_X_TOKEN }} JWT_SECRET: ${{ secrets.JWT_SECRET }} PASSWORD_SALT: ${{ secrets.PASSWORD_SALT }} REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }} RABBITMQ_PASSWORD: ${{ secrets.RABBITMQ_PASSWORD }} RASA_X_ADMIN_PASSWORD: ${{ secrets.RASA_X_ADMIN_PASSWORD }} GKE_PROJECT: ${{ secrets.GKE_PROJECT }} FACEBOOK_VERIFY_TOKEN: ${{ secrets.FACEBOOK_VERIFY_TOKEN }} FACEBOOK_APP_SECRET: ${{ secrets.FACEBOOK_APP_SECRET }} FACEBOOK_PAGE_ACCESS_TOKEN: ${{ secrets.FACEBOOK_PAGE_ACCESS_TOKEN }} CLIMATIQ_API_KEY: ${{ secrets.CLIMATIQ_API_KEY }} run: | # Install helm v3 curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash # Read and export variables from .env file set -o allexport; source .env; set +o allexport kubectl create ns ${NAMESPACE} || true cd .github/deployments; helmfile sync cat <<EOF | kubectl apply --namespace ${NAMESPACE} -f - apiVersion: networking.gke.io/v1beta1 kind: ManagedCertificate metadata: generation: 1 name: rasa-bots-certificate spec: domains: - ${{ env.DOMAIN }} EOF upload-model: name: Upload the trained model to Rasa X needs: - deploy-to-cluster - build-model env: MODEL_DIRECTORY: "models" if: github.event_name == 'push' && (startsWith(github.event.ref, 'refs/tags') || github.ref == 'refs/heads/master') runs-on: ubuntu-latest steps: - name: Download Model uses: actions/download-artifact@v2 with: name: model path: ${{ env.MODEL_DIRECTORY }} - name: Get path to model run: | ls -R echo "MODELNAME=${{ env.MODEL_DIRECTORY }}/$(ls ${{ env.MODEL_DIRECTORY }})" >> $GITHUB_ENV - name: Upload Model to Rasa run: | # Get token RASA_X_TOKEN=$(curl -s --header "Content-Type: application/json" \ --request POST \ --data "{\"username\":\"${RASA_X_USERNAME}\",\"password\":\"${RASA_X_PASSWORD}\"}" \ https://${{ env.DOMAIN }}/api/auth | jq -r .access_token) # Upload model curl -k --fail -H "Authorization: Bearer ${RASA_X_TOKEN}" -F "model=@${MODELNAME}" https://${{ env.DOMAIN }}/api/projects/default/models # ensure model is ready and tag as production sleep 5 export MODEL=$(basename ${MODELNAME} .tar.gz) curl --fail -XPUT -H "Authorization: Bearer ${RASA_X_TOKEN}" https://${{ env.DOMAIN }}/api/projects/default/models/${MODEL}/tags/production
https://github.com/RasaHQ/rasa-demo/blob/main/.github/workflows/continuous_integration.yml
name: Continuous Integration on: [pull_request] env: GDRIVE_CREDENTIALS: ${{ secrets.GDRIVE_CREDENTIALS }} MAILCHIMP_LIST: ${{ secrets.MAILCHIMP_LIST }} MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} ALGOLIA_SEARCH_KEY: ${{ secrets.ALGOLIA_SEARCH_KEY }} ALGOLIA_DOCS_INDEX: ${{ secrets.ALGOLIA_DOCS_INDEX }} RASA_X_HOST: ${{ secrets.RASA_X_DOMAIN }} RASA_X_PASSWORD: ${{ secrets.RASA_X_PASSWORD }} RASA_X_USERNAME: ${{ secrets.RASA_X_USERNAME }} RASA_X_HOST_SCHEMA: ${{ secrets.RASA_X_HOST_SCHEMA }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TRACKER_DB_URL: ${{ secrets.TRACKER_DB_URL }} # Due to the issue with openssl library for Google Cloud SDK (gcloud) # (https://github.com/GoogleCloudPlatform/github-actions/issues/128) # we use 297.0.01 version GCLOUD_VERSION: "297.0.1" concurrency: group: ci-${{ github.ref }} cancel-in-progress: true jobs: check_changed_files: name: Check for file changes runs-on: ubuntu-20.04 outputs: nlu: ${{ steps.changed-files.outputs.nlu }} core: ${{ steps.changed-files.outputs.core }} training: ${{ steps.changed-files.outputs.training }} actions: ${{ steps.changed-files.outputs.actions }} steps: # Due to an issue with checking out a wrong commit, we make sure # to checkout HEAD commit for a pull request. # More details: https://github.com/actions/checkout/issues/299 - name: Checkout pull request HEAD commit instead of merge commit uses: actions/checkout@v2 if: github.event_name == 'pull_request' with: ref: ${{ github.event.pull_request.head.sha }} - name: Checkout git repository uses: actions/checkout@v2 if: github.event_name != 'pull_request' - uses: RasaHQ/pr-changed-files-filter@c4f7116a04b8a4596313469429e2ad235f59d9c4 id: changed-files with: token: ${{ secrets.GITHUB_TOKEN }} filters: .github/change_filters.yml base: ${{ github.ref }} lint-testing: name: Code Formatting Tests runs-on: ubuntu-latest steps: - name: Checkout pull request HEAD commit instead of merge commit uses: actions/checkout@v2 if: github.event_name == 'pull_request' with: ref: ${{ github.event.pull_request.head.sha }} - name: Checkout git repository uses: actions/checkout@v2 if: github.event_name != 'pull_request' - name: Set up Python 3.7 uses: actions/setup-python@v1 with: python-version: 3.7 - name: Install dependencies run: | python -m pip install --upgrade "pip<20" pip install -r requirements-dev.txt - name: Code Formatting Tests run: | echo "------------------------------------" echo "/usr/bin/git log -1 --format='%H'" /usr/bin/git log -1 --format='%H' echo "------------------------------------" make lint type-testing: name: Type Tests runs-on: ubuntu-latest steps: - name: Checkout pull request HEAD commit instead of merge commit uses: actions/checkout@v2 if: github.event_name == 'pull_request' with: ref: ${{ github.event.pull_request.head.sha }} - name: Checkout git repository uses: actions/checkout@v2 if: github.event_name != 'pull_request' - name: Set up Python 3.7 uses: actions/setup-python@v1 with: python-version: 3.7 - name: Install dependencies run: | python -m pip install --upgrade "pip<20" pip install -r requirements-dev.txt - name: Type Checking run: | pip list make types action-unit-tests: needs: - lint-testing - type-testing name: Custom Action Unit Tests runs-on: ubuntu-latest steps: - name: Checkout pull request HEAD commit instead of merge commit uses: actions/checkout@v2 if: github.event_name == 'pull_request' with: ref: ${{ github.event.pull_request.head.sha }} - name: Checkout git repository uses: actions/checkout@v2 if: github.event_name != 'pull_request' - name: Set up Python 3.7 uses: actions/setup-python@v1 with: python-version: 3.7 - name: Install dependencies run: | make install-dev - name: Unit Tests run: | make test-actions data-validation: name: Data Validation runs-on: ubuntu-latest steps: - name: Checkout pull request HEAD commit instead of merge commit uses: actions/checkout@v2 if: github.event_name == 'pull_request' with: ref: ${{ github.event.pull_request.head.sha }} - name: Checkout git repository uses: actions/checkout@v2 if: github.event_name != 'pull_request' - name: Set up Python 3.7 uses: actions/setup-python@v1 with: python-version: 3.7 - name: Install dependencies run: | python -m pip install --upgrade "pip<20" pip install -r requirements-dev.txt - name: Rasa Data Validation run: | rasa data validate --debug training-testing: name: Test Model runs-on: ubuntu-latest needs: - data-validation - check_changed_files if: ${{ needs.check_changed_files.outputs.training == 'true' }} steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.8.0 with: access_token: ${{ github.token }} - name: Checkout pull request HEAD commit instead of merge commit uses: actions/checkout@v2 if: github.event_name == 'pull_request' with: ref: ${{ github.event.pull_request.head.sha }} - name: Checkout git repository uses: actions/checkout@v2 if: github.event_name != 'pull_request' - name: Set up Python 3.7 uses: actions/setup-python@v1 with: python-version: 3.7 - name: Install dependencies run: | python -m pip install --upgrade "pip<20" pip install -r requirements-dev.txt rasa --version - name: Cross-validate NLU model id: cvnlu if: contains( github.event.pull_request.labels.*.name, 'nlu_testing_required' ) run: | rasa --version rasa test nlu -f 3 --cross-validation --config config_nlu_testing.yml python .github/workflows/format_results.py - name: post cross-val results to PR if: steps.cvnlu.outcome == 'success' uses: amn41/comment-on-pr@comment-file-contents continue-on-error: true with: msg: results.md - name: Train Model run: | rasa --version rasa train - name: Test End 2 End Stories if: ${{ needs.check_changed_files.outputs.core == 'true' }} run: | rasa --version rasa test core --stories tests/test_conversations.yml --fail-on-prediction-errors - name: Wait for the conclusion of all other workflow runs # upload model from PR if: github.event_name == 'pull_request' id: check-runs-conclusion env: WAIT_INTERVAL_SECS: 10 timeout-minutes: 20 run: | while true; do # Get a list of checks information, excluding training-testing and build-images CHECKS_LIST=$(gh api /repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs --jq '.check_runs.[] | select(.name != "Test Model" and .name != "Build Action Server Image")') # Get the status and conclusion of echo jobs STATUS_LIST=$(echo $CHECKS_LIST | jq -r '.status') CONCLUSION_LIST=$(echo $CHECKS_LIST | jq -r '.conclusion') # Make sure all other check runs are completed if [[ "$(echo $STATUS_LIST | tr ' ' '\n' | sort | uniq)" == "completed" ]]; then # Check the conclusion of all other check runs # Fail the step if there is any failture if [[ "$(echo CONCLUSION_LIST | tr ' ' '\n' | sort | uniq)" =~ "failure" ]]; then echo "::error:: Some check runs failed. Skip uploading model." exit 1 else echo "All other check runs are successed." echo "::set-output name=upload-model::true" exit 0 fi fi sleep $WAIT_INTERVAL_SECS echo "Wait for $WAIT_INTERVAL_SECS seconds..." done - name: Set model name from Rasa version if: | github.event_name == 'pull_request' && steps.check-runs-conclusion.outputs.upload-model == 'true' run: | python -c "import rasa; open('rasaversion.txt','w+').write(rasa.__version__)" rasa_version=`cat rasaversion.txt` model_path=`ls models/*.tar.gz | head -n 1` model_timestamp=$(basename "$model_path" .tar.gz) model_name="$model_timestamp"_rasa"$rasa_version" renamed_model_path=models/"$model_name".tar.gz mv $model_path $renamed_model_path echo "MODEL_NAME=${model_name}" >> $GITHUB_ENV echo "MODEL_PATH=${renamed_model_path}" >> $GITHUB_ENV - uses: google-github-actions/setup-gcloud@master if: | (github.event_name == 'pull_request' && contains( github.event.pull_request.labels.*.name, 'upload_model' )) || steps.check-runs-conclusion.outputs.upload-model == 'true' name: Authenticate with gcloud 🎫 with: version: "${{ env.GCLOUD_VERSION }}" service_account_email: ${{ secrets.SARA_GKE_SERVICE_ACCOUNT_NAME }} service_account_key: ${{ secrets.SARA_GKE_SERVICE_ACCOUNT_KEY }} - name: Upload model to storage bucket if: | (github.event_name == 'pull_request' && contains( github.event.pull_request.labels.*.name, 'upload_model' )) || steps.check-runs-conclusion.outputs.upload-model == 'true' run: gsutil cp "${MODEL_PATH}" ${{ secrets.STORAGE_BUCKET_URL }}/rasa_demo_models build-images: name: Build Action Server Image needs: - lint-testing - type-testing - training-testing - check_changed_files if: ${{ needs.check_changed_files.outputs.actions == 'true' }} runs-on: ubuntu-latest steps: - name: Checkout pull request HEAD commit instead of merge commit uses: actions/checkout@v2 if: github.event_name == 'pull_request' with: ref: ${{ github.event.pull_request.head.sha }} - name: Checkout git repository uses: actions/checkout@v2 if: github.event_name != 'pull_request' - name: Authenticate with gcloud 🎫 uses: google-github-actions/setup-gcloud@daadedc81d5f9d3c06d2c92f49202a3cc2b919ba with: version: ${{ env.GCLOUD_VERSION }} service_account_key: ${{ secrets.GCLOUD_AUTH }} - name: Configure Docker to use Google Cloud Platform run: | gcloud auth configure-docker - name: Pull Latest Image run: | docker pull gcr.io/replicated-test/rasa-demo:latest || true - name: Build Image run: | docker build --cache-from gcr.io/replicated-test/rasa-demo:latest .