Appsody - Deploy to remote cluster

Recently updated to include RedHat OpenShift 4.x

Configure Image Registry

We are going to use a managed OpenShift cluter on IBM Cloud

Follow the documentation https://cloud.ibm.com/docs/openshift?topic=openshift-openshift-images to expose the internal image registry, this will allow you to push images from your local workstation to the remote registry.

Here is the TLDR; for this section

  • Setup docker login for CRC (Code Ready Containers)

export INTERNAL_IMAGE_REGISTRY=image-registry.openshift-image-registry.svc:5000
export IMAGE_REGISTRY=$(oc get route default-route  -n openshift-image-registry -o jsonpath="{.spec.host}")
docker login -u kubeadmin -p $(oc whoami -t) $IMAGE_REGISTRY
  • Create a route for OCP 4.x

oc create route reencrypt image-registry --service=image-registry -n openshift-image-registry
oc patch route image-registry -n openshift-image-registry --type='json' -p='[{"op": "add", "path": "/metadata/annotations/haproxy.router.openshift.io~1balance", "value":"source"}]'
export INTERNAL_IMAGE_REGISTRY=image-registry.openshift-image-registry.svc:5000
export IMAGE_REGISTRY=$(oc get route image-registry -n openshift-image-registry -o jsonpath="{.spec.host}")
docker login -u $(oc whoami) -p $(oc whoami -t) $IMAGE_REGISTRY
Docker login not working with oc whoami

If you are using the kubeadmin account on OCP 4.x, the command oc whoami returns kube:admin and this value will not let you login using docker cli, to workaround use the username kubeadmin explicit in the command

docker login -u kubeadmin -p $(oc whoami -t) $IMAGE_REGISTRY
  • Create a route for OCP 3.11

oc create route reencrypt docker-registry --service=docker-registry -n default
oc patch route docker-registry -n default --type='json' -p='[{"op": "add", "path": "/metadata/annotations/haproxy.router.openshift.io~1balance", "value":"source"}]'
export INTERNAL_IMAGE_REGISTRY=docker-registry.default.svc:5000
export IMAGE_REGISTRY=$(oc get route docker-registry -n default -o jsonpath="{.spec.host}")
docker login -u $(oc whoami) -p $(oc whoami -t) $IMAGE_REGISTRY
Working with insecured image registries

If using the IBM Cloud OpenShift you should not have problems with docker login, but if you are using another opnshift cluster and get an error about https/ssl., then add the registry to the Docker Engine config (ie Docker→Preferences→Docker Engine) for example:

{
  "insecure-registries": [
    "default-route-openshift-image-registry.apps-crc.testing",
    "image-registry-openshift-image-registry.apps.foobar.os.fyre.ibm.com",
    "1.2.3.4.nip.io:5000"
  ]
}

Create Application

Create a new appsody application, or use existing one.

mkdir appsody-app
cd appsody-app
appsody init kabanero/nodejs-express

Setup Application manifest

Create a namespace to deploy the application for personal dev, for example my-project

oc new-project my-project
export NAMESPACE=$(oc project -q)

Generate an appsody deploy yaml and rename it to app-deploy-remote.yaml

appsody deploy --generate-only -n $NAMESPACE

Deploy the Application

Build and push the application image to the remote registry

export APP_NAME=$(yq r app-deploy.yaml metadata.name)
export IMAGE_NAME=${APP_NAME}
export IMAGE_TAG=${RANDOM}
appsody deploy \
  --tag "${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" \
  --push-url "${IMAGE_REGISTRY}" \
  --pull-url "${INTERNAL_IMAGE_REGISTRY}"
  -n ${NAMESPACE}

If you want to use Serverless then append --knative to the appsody deploy

export APP_NAME=$(yq r app-deploy.yaml metadata.name)
export IMAGE_NAME=${APP_NAME}
export IMAGE_TAG=${RANDOM}
appsody deploy \
  --tag "${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}" \
  --push-url "${IMAGE_REGISTRY}" \
  --pull-url "${INTERNAL_IMAGE_REGISTRY}"
  -n ${NAMESPACE} --knative

Access the Application

If not using knative

export APP_URL=http://$(oc get route ${APP_NAME} -n ${NAMESPACE} -o jsonpath="{.spec.host}")

If using knative

export APP_URL=$(oc get ksvc ${APP_NAME} -n ${NAMESPACE} -o jsonpath="{.status.url}")

Open with a browser or use curl

open ${APP_URL}
curl ${APP_URL}

Automate appsody deploy

Create a .env file and a script deploy.sh. Use ./deploy.sh everytime you want to deploy to the remote cluster

Run the following script in your terminal to create the two files .env and deploy.sh

appsody deploy --generate-only -n $NAMESPACE

cat <<EOF >.env
INTERNAL_IMAGE_REGISTRY=${INTERNAL_IMAGE_REGISTRY}
IMAGE_REGISTRY=${IMAGE_REGISTRY}
NAMESPACE=${NAMESPACE}
APP_NAME=$(yq r app-deploy.yaml metadata.name)
IMAGE_NAME=\${APP_NAME}
APP_KNATIVE=false
EOF

cat <<EOF >deploy.sh
#!/bin/bash
source .env
IMAGE_TAG=\${RANDOM}

if ! oc get project \${NAMESPACE}; then
  echo project \${NAMESPACE} not found, creating new project \${NAMESPACE}
  oc new-project \${NAMESPACE}
fi

if [ "\$APP_KNATIVE" = "true" ]; then
  echo Deploying Serverless Service
  APP_KNATIVE_FLAG="--knative"
fi

appsody deploy \
  --tag \${NAMESPACE}/\${IMAGE_NAME}:\${IMAGE_TAG} \
  --push-url \${IMAGE_REGISTRY} \
  --pull-url \${INTERNAL_IMAGE_REGISTRY} \
  -n \${NAMESPACE} \${APP_KNATIVE_FLAG}

if [ "\$APP_KNATIVE" = "true" ]; then
  echo Getting Serveless Application URL...
  APP_URL=\$(oc get ksvc \${APP_NAME} -n \${NAMESPACE} -o jsonpath="{.status.url}")
else
  echo Getting Application URL...
  APP_URL=http://\$(oc get route \${APP_NAME} -n \${NAMESPACE} -o jsonpath="{.spec.host}")
fi

echo App deployed: \${APP_URL}
EOF
chmod +x deploy.sh

You can automatically run deploy.sh on file change. You can use an utility like appsody watcher.

Install a tool to watch files and run a command when a file changes. For example fswatch

# install appsody fswatch binary
brew install fswatch

Run the following command excluding the directory .git/ and the file app-deploy.yaml

fswatch -e .git/ -e app-deploy.yaml -o . | xargs -n1 -I{} "./deploy.sh"
  • You should use appsody run most of the time to work with your application locally, if there is a need to deploy to a remote cluster then use ./deploy.sh.

  • The best practice is to push your code to a git repository, and letting the devops process take over to deploy to the cluster using one of these workflows: