ODO and Rancher Desktop

odo is a fast, iterative and straightforward CLI tool for developers who write, build, and deploy applications on Kubernetes. The odo CLI abstracts away complex Kubernetes concepts and allows developers to focus on iterating code. The helper tool can detect changes to local code and deploy them to a container orchestrated cluster automatically, giving instant feedback to validate changes in real-time. Please refer to the odo project documentation to learn more.

Prerequisites

For this guide you will use the express-sample node.js application in the Rancher Desktop documentation repository as a way to demonstrate the use of odo and Rancher Desktop.

ODO and Rancher Desktop - 图1note

odo works with the dockerd (moby) runtime, be sure to have it selected from the preferences dialog location Preferences > Container Engine > Allowed Images.

Please ensure that Kubernetes is enabled for your application. Additionally, in order to use odo deploy, you will need to be able to build and push an image to a Docker container registry. Log in using your Docker credentials as noted below:

  1. $ docker login docker.io
  2. Username:
  3. Password:
  4. Login Succeeded!

Installation

Install odo by visiting https://odo.dev/docs/overview/installation and perform the appropriate install for your platform. The tool can be used both as a CLI tool or an IDE plugin, as well as a few alternative install methods depending on your preference. This guide will focus on using the tool through the CLI.

Steps: odo init

This command will initialize the application by creating a devfile.yaml for deployments.

  1. Clone the Rancher Desktop documentation repository and change your directory to the sample-express application.
  1. git clone https://github.com/rancher-sandbox/docs.rancherdesktop.io.git
  2. cd docs.rancherdesktop.io/assets/express-sample
  1. Before initializing, you must connect odo to your cluster via a namespace, which can be created with the command odo create namespace :
  1. odo create namespace odo-dev

Sample Output

  1. $ odo create namespace odo-dev
  2. Creating the namespace "odo-dev" [5ms]
  3. Namespace "odo-dev" is ready for use
  4. New namespace created and now using namespace: odo-dev
  1. The command odo init will auto-detect your project framework and choose the appropriate devfile.yaml to be used for deployment of your application. The command will allow you to confirm the Devfile (Y/n), select a container to change configuration (choose none for this example), and enter a component name (e.g. my-nodejs-app).

Alternatively, the following command with the additional flags (e.g. --devfile-version 2.2.0) can be used to initialize odo and allow your application to be deployed:

  1. odo init --name my-nodejs-app --devfile nodejs --devfile-registry DefaultDevfileRegistry --devfile-version 2.2.0

Sample Output

  1. __
  2. / \__ Initializing a new component
  3. \__/ \
  4. / \__/ odo version: v3.13.0
  5. \__/
  6. Downloading devfile "nodejs:2.2.0" from registry "DefaultDevfileRegistry" [1s]
  7. Your new component 'my-nodejs-app' is ready in the current directory.
  8. To start editing your component, use 'odo dev' and open this folder in your favorite IDE.
  9. Changes will be directly reflected on the cluster.
  1. odo init

Sample Output

  1. $ odo init
  2. __
  3. / \__ Initializing a new component
  4. \__/ \ Files: Source code detected, a Devfile will be determined based upon source code autodetection
  5. / \__/ odo version: v3.13.0
  6. \__/
  7. Interactive mode enabled, please answer the following questions:
  8. Determining a Devfile for the current directory [910ms]
  9. Based on the files in the current directory odo detected
  10. Language: JavaScript
  11. Project type: Node.js
  12. Application ports: 3000
  13. The devfile "nodejs:2.1.1" from the registry "DefaultDevfileRegistry" will be downloaded.
  14. ? Is this correct? Yes
  15. Downloading devfile "nodejs:2.1.1" from registry "DefaultDevfileRegistry" [933ms]
  16. Container Configuration "runtime":
  17. OPEN PORTS:
  18. - 3000
  19. - 5858
  20. ENVIRONMENT VARIABLES:
  21. - DEBUG_PORT = 5858
  22. ? Select container for which you want to change configuration? NONE - configuration is correct
  23. ? Enter component name: my-nodejs-app
  24. You can automate this command by executing:
  25. odo init --name my-nodejs-app --devfile nodejs --devfile-registry DefaultDevfileRegistry --devfile-version 2.1.1
  26. Your new component 'my-nodejs-app' is ready in the current directory.
  27. To start editing your component, use 'odo dev' and open this folder in your favorite IDE.
  28. Changes will be directly reflected on the cluster.

Steps: odo dev

Now, you can run the command odo dev to continuously deploy applications as you make changes to your code through your preferred IDE.

ODO and Rancher Desktop - 图2caution

You may run into an ErrImagePull error as the image may not be covered by Rancher Desktop’s allowed images list. To resolve the error, please add the necessary image in Preferences > Container Engine > Allowed Images and hit apply to update allowed images immediately.

  1. odo dev

Sample Output

  1. $ odo dev
  2. __
  3. / \__ Developing using the "my-nodejs-app" Devfile
  4. \__/ \ Namespace: odo-dev
  5. / \__/ odo version: v3.13.0
  6. \__/
  7. Running on the cluster in Dev mode
  8. I0728 13:50:53.115137 92567 starterserver.go:123] API Server started at localhost:20000/api/v1
  9. Waiting for Kubernetes resources ...
  10. Pod is Pending
  11. Pod is Running
  12. Syncing files into the container [306ms]
  13. Building your application in container (command: install) [3s]
  14. Executing the application (command: run) ...
  15. Waiting for the application to be ready [1s]
  16. - Forwarding from 127.0.0.1:20001 -> 3000
  17. Dev mode
  18. Status:
  19. Watching for changes in the current directory /Users/docs.rancherdesktop.io/assets/express-sample
  20. Keyboard Commands:
  21. [Ctrl+c] - Exit and delete resources from the cluster
  22. [p] - Manually apply local changes to the application on the cluster

The express-sample application can now be accessed by the local port (127.0.0.1:20001). As an example, you can make a text change to the index.jade file in the views folder to see a real-time update to the application.

Steps: odo deploy

This command will deploy your application to your cluster with instructions from your devfile.yaml.

  1. Be sure to be logged into the Docker container registry to push the application to, and set your container image build arguments to be the same as your container architecture using the ODO_IMAGE_BUILD_ARGS environment variable:
  • AMD64
  • ARM
  1. export ODO_IMAGE_BUILD_ARGS="--platform=linux/amd64"
  1. export ODO_IMAGE_BUILD_ARGS="--platform=linux/arm64"
  1. Update the Dockerfile in the express-sample directory to containerize the application with correct variables in order to build and push to a registry:

Sample Dockerfile

  1. # Install the app dependencies in a full SLE Node image
  2. FROM registry.suse.com/bci/nodejs:16
  3. # Copy package.json and package-lock.json
  4. COPY package*.json ./
  5. # Install app dependencies
  6. RUN npm install --production
  7. # Install app dependencies
  8. COPY . /opt/app-root/src
  9. ENV NODE_ENV production
  10. ENV PORT 3000
  11. CMD ["npm", "start"]
  1. Modify the devfile.yaml to the example noted below for your container cluster.
  • Update the variables to access your container registry:
  1. # Add the following variables code anywhere in devfile.yaml
  2. # This MUST be a container registry you are able to access
  3. variables:
  4. CONTAINER_IMAGE: docker.io/<INSERTUSERNAME>/nodejs-odo-example
  5. RESOURCE_NAME: my-nodejs-app
  6. CONTAINER_PORT: "3000"
  7. DOMAIN_NAME: nodejs.example.com
  • Update the Devfile schema to 2.2.0 as odo deploy makes use of this version. Additionally, there is a command to initialize odo with the correct schemaVersion: 2.2.0 noted above in the installation:
  1. # Deploy "kind" ID's use schema 2.2.0+
  2. schemaVersion: 2.2.0
  • The commands seen below are used for deployment activities:

Deployment Commands

  1. # This is the main "composite" command that will run all below commands
  2. commands:
  3. - id: deploy
  4. composite:
  5. commands:
  6. - build-image
  7. - k8s-deployment
  8. - k8s-service
  9. - k8s-url
  10. group:
  11. isDefault: true
  12. kind: deploy
  13. # Below are the commands and their respective components that they are "linked" to deploy
  14. - id: build-image
  15. apply:
  16. component: outerloop-build
  17. - id: k8s-deployment
  18. apply:
  19. component: outerloop-deployment
  20. - id: k8s-service
  21. apply:
  22. component: outerloop-service
  23. - id: k8s-url
  24. apply:
  25. component: outerloop-url
  • The commands seen below are used for adding the Docker image location, K8s deployment and services to components:

Component Commands

  1. # This will build the container image before deployment
  2. - name: outerloop-build
  3. image:
  4. dockerfile:
  5. buildContext: ${PROJECT_SOURCE}
  6. rootRequired: false
  7. uri: ./Dockerfile
  8. imageName: "{{CONTAINER_IMAGE}}"
  9. # This will create a Deployment in order to run your container image across
  10. # the cluster.
  11. - name: outerloop-deployment
  12. kubernetes:
  13. inlined: |
  14. kind: Deployment
  15. apiVersion: apps/v1
  16. metadata:
  17. name: {{RESOURCE_NAME}}
  18. spec:
  19. replicas: 1
  20. selector:
  21. matchLabels:
  22. app: {{RESOURCE_NAME}}
  23. template:
  24. metadata:
  25. labels:
  26. app: {{RESOURCE_NAME}}
  27. spec:
  28. containers:
  29. - name: {{RESOURCE_NAME}}
  30. image: {{CONTAINER_IMAGE}}
  31. ports:
  32. - name: http
  33. containerPort: {{CONTAINER_PORT}}
  34. protocol: TCP
  35. resources:
  36. limits:
  37. memory: "1024Mi"
  38. cpu: "500m"
  39. # This will create a Service so your Deployment is accessible.
  40. # Depending on your cluster, you may modify this code so it's a
  41. # NodePort, ClusterIP or a LoadBalancer service.
  42. - name: outerloop-service
  43. kubernetes:
  44. inlined: |
  45. apiVersion: v1
  46. kind: Service
  47. metadata:
  48. name: {{RESOURCE_NAME}}
  49. spec:
  50. ports:
  51. - name: "{{CONTAINER_PORT}}"
  52. port: {{CONTAINER_PORT}}
  53. protocol: TCP
  54. targetPort: {{CONTAINER_PORT}}
  55. selector:
  56. app: {{RESOURCE_NAME}}
  57. type: NodePort
  • The last addition to our Devfile is adding the Kubernetes ingress component as noted below:

Ingress Commands

  1. - name: outerloop-url
  2. kubernetes:
  3. inlined: |
  4. apiVersion: networking.k8s.io/v1
  5. kind: Ingress
  6. metadata:
  7. name: {{RESOURCE_NAME}}
  8. spec:
  9. rules:
  10. - host: "{{DOMAIN_NAME}}"
  11. http:
  12. paths:
  13. - path: "/"
  14. pathType: Prefix
  15. backend:
  16. service:
  17. name: {{RESOURCE_NAME}}
  18. port:
  19. number: {{CONTAINER_PORT}}
  • Below is the example devfile.yaml that you can use to help illustrate command and variable settings after they are all put together. Please review your Devfile to match or update the appropriate variables as noted below:

Final Devfile

  1. commands:
  2. - exec:
  3. commandLine: npm install
  4. component: runtime
  5. group:
  6. isDefault: true
  7. kind: build
  8. workingDir: ${PROJECT_SOURCE}
  9. id: install
  10. - exec:
  11. commandLine: npm start
  12. component: runtime
  13. group:
  14. isDefault: true
  15. kind: run
  16. workingDir: ${PROJECT_SOURCE}
  17. id: run
  18. - exec:
  19. commandLine: npm run debug
  20. component: runtime
  21. group:
  22. isDefault: true
  23. kind: debug
  24. workingDir: ${PROJECT_SOURCE}
  25. id: debug
  26. - exec:
  27. commandLine: npm test
  28. component: runtime
  29. group:
  30. isDefault: true
  31. kind: test
  32. workingDir: ${PROJECT_SOURCE}
  33. id: test
  34. # This is the main "composite" command that will run all below commands
  35. - id: deploy
  36. composite:
  37. commands:
  38. - build-image
  39. - k8s-deployment
  40. - k8s-service
  41. - k8s-url
  42. group:
  43. isDefault: true
  44. kind: deploy
  45. # Below are the commands and their respective components that they are "linked" to deploy
  46. - id: build-image
  47. apply:
  48. component: outerloop-build
  49. - id: k8s-deployment
  50. apply:
  51. component: outerloop-deployment
  52. - id: k8s-service
  53. apply:
  54. component: outerloop-service
  55. - id: k8s-url
  56. apply:
  57. component: outerloop-url
  58. components:
  59. - container:
  60. args:
  61. - tail
  62. - -f
  63. - /dev/null
  64. endpoints:
  65. - name: http-node
  66. targetPort: 3000
  67. - exposure: none
  68. name: debug
  69. targetPort: 5858
  70. env:
  71. - name: DEBUG_PORT
  72. value: "5858"
  73. image: registry.suse.com/bci/nodejs:16:latest
  74. memoryLimit: 1024Mi
  75. mountSources: true
  76. name: runtime
  77. # This will build the container image before deployment
  78. - name: outerloop-build
  79. image:
  80. dockerfile:
  81. buildContext: ${PROJECT_SOURCE}
  82. rootRequired: false
  83. uri: ./Dockerfile
  84. imageName: "{{CONTAINER_IMAGE}}"
  85. # This will create a Deployment in order to run your container image across
  86. # the cluster.
  87. - name: outerloop-deployment
  88. kubernetes:
  89. inlined: |
  90. kind: Deployment
  91. apiVersion: apps/v1
  92. metadata:
  93. name: {{RESOURCE_NAME}}
  94. spec:
  95. replicas: 1
  96. selector:
  97. matchLabels:
  98. app: {{RESOURCE_NAME}}
  99. template:
  100. metadata:
  101. labels:
  102. app: {{RESOURCE_NAME}}
  103. spec:
  104. containers:
  105. - name: {{RESOURCE_NAME}}
  106. image: {{CONTAINER_IMAGE}}
  107. ports:
  108. - name: http
  109. containerPort: {{CONTAINER_PORT}}
  110. protocol: TCP
  111. resources:
  112. limits:
  113. memory: "1024Mi"
  114. cpu: "500m"
  115. # This will create a Service so your Deployment is accessible.
  116. # Depending on your cluster, you may modify this code so it's a
  117. # NodePort, ClusterIP or a LoadBalancer service.
  118. - name: outerloop-service
  119. kubernetes:
  120. inlined: |
  121. apiVersion: v1
  122. kind: Service
  123. metadata:
  124. name: {{RESOURCE_NAME}}
  125. spec:
  126. ports:
  127. - name: "{{CONTAINER_PORT}}"
  128. port: {{CONTAINER_PORT}}
  129. protocol: TCP
  130. targetPort: {{CONTAINER_PORT}}
  131. selector:
  132. app: {{RESOURCE_NAME}}
  133. type: NodePort
  134. - name: outerloop-url
  135. kubernetes:
  136. inlined: |
  137. apiVersion: networking.k8s.io/v1
  138. kind: Ingress
  139. metadata:
  140. name: {{RESOURCE_NAME}}
  141. spec:
  142. rules:
  143. - host: "{{DOMAIN_NAME}}"
  144. http:
  145. paths:
  146. - path: "/"
  147. pathType: Prefix
  148. backend:
  149. service:
  150. name: {{RESOURCE_NAME}}
  151. port:
  152. number: {{CONTAINER_PORT}}
  153. metadata:
  154. description: Stack with Node.js 16
  155. displayName: Node.js Runtime
  156. icon: https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg
  157. language: JavaScript
  158. name: my-node-app
  159. projectType: Node.js
  160. tags:
  161. - Node.js
  162. - Express
  163. - ubi8
  164. version: 2.1.1
  165. schemaVersion: 2.2.0
  166. starterProjects:
  167. - git:
  168. remotes:
  169. origin: https://github.com/odo-devfiles/nodejs-ex.git
  170. name: nodejs-starter
  171. # Add the following variables code anywhere in devfile.yaml
  172. # This MUST be a container registry you are able to access
  173. variables:
  174. CONTAINER_IMAGE: docker.io/<INSERTUSERNAME>/node-odo-example
  175. RESOURCE_NAME: my-node-app
  176. CONTAINER_PORT: "3000"
  177. DOMAIN_NAME: node.example.com
  1. Now, you can run the command odo deploy to deploy the application to the cluster:

ODO and Rancher Desktop - 图3caution

You may run into an unauthorized: image error as the image may not be covered by Rancher Desktop’s allowed images list. To resolve the error, please add the necessary image in Preferences > Container Engine > Allowed Images and hit apply to update allowed images immediately.

Sample Output

  1. $ odo deploy
  2. __
  3. / \__ Running the application in Deploy mode using my-node-app Devfile
  4. \__/ \ Namespace: odo-dev
  5. / \__/ odo version: v3.13.0
  6. \__/
  7. Building & Pushing Image: docker.io/arjsin/nodejs-odo-example
  8. Building image locally ...
  9. [+] Building 2.7s (9/9) FINISHED
  10. => [internal] load build definition from Dockerfile 0.0s
  11. => => transferring dockerfile: 405B 0.0s
  12. => [internal] load .dockerignore 0.0s
  13. => => transferring context: 364B 0.0s
  14. => [internal] load metadata for registry.suse.com/bci/nodejs:16 2.2s
  15. => [1/4] FROM registry.suse.com/bci/nodejs:16@sha256:dda0e616a0fcb3dc589 0.0s
  16. => [internal] load build context 0.0s
  17. => => transferring context: 5.14kB 0.0s
  18. => CACHED [2/4] COPY package*.json ./ 0.0s
  19. => CACHED [3/4] RUN npm install --production 0.0s
  20. => [4/4] COPY . /opt/app-root/src 0.0s
  21. => exporting to image 0.4s
  22. => => exporting layers 0.4s
  23. => => writing image sha256:c6d3ed7d9fb4736d3c4e95b54054533f79d64d3a01e65 0.0s
  24. => => naming to docker.io/arjsin/nodejs-odo-example 0.0s
  25. Building image locally [3s]
  26. Pushing image to container registry ...
  27. Using default tag: latest
  28. The push refers to repository [docker.io/arjsin/nodejs-odo-example]
  29. 20658d9b13ba: Pushed
  30. 7b1ee26c3aea: Pushed
  31. 067890bef08d: Pushed
  32. d08e96dfc7bc: Pushed
  33. 174c0e293bd0: Pushed
  34. latest: digest: sha256:ca598fc0c5278e8d00cba41e14914f1d3f7a3561bd4a324f2ffcd33b166135ad size: 1368
  35. Pushing image to container registry [30s]
  36. Deploying Kubernetes Component: my-node-app
  37. Creating resource Deployment/my-node-app
  38. Deploying Kubernetes Component: my-node-app
  39. Creating resource Service/my-node-app
  40. Deploying Kubernetes Component: my-node-app
  41. Creating resource Ingress/my-node-app
  42. Your Devfile has been successfully deployed

Steps: odo describe component

Now, the command odo describe component can be used to view information from the Devfile such as Kubernetes components, ingresses, and the URL to access the application:

  1. odo describe component

Sample Output

  1. $ odo describe component
  2. Name: my-nodejs-app
  3. Display Name: Node.js Runtime
  4. Project Type: Node.js
  5. Language: JavaScript
  6. Version: 2.2.0
  7. Description: Node.js 18 application
  8. Tags: Node.js, Express, ubi8
  9. Running in: None
  10. Supported odo features:
  11. Dev: true
  12. Deploy: false
  13. Debug: true
  14. Commands:
  15. install
  16. Type: exec
  17. Group: build
  18. Command Line: "npm install"
  19. Component: runtime
  20. Component Type: container
  21. run
  22. Type: exec
  23. Group: run
  24. Command Line: "npm start"
  25. Component: runtime
  26. Component Type: container
  27. debug
  28. Type: exec
  29. Group: debug
  30. Command Line: "npm run debug"
  31. Component: runtime
  32. Component Type: container
  33. test
  34. Type: exec
  35. Group: test
  36. Command Line: "npm test"
  37. Component: runtime
  38. Component Type: container
  39. Container components:
  40. runtime
  41. Source Mapping: /projects

Steps: odo delete component

After you have completed testing, you can free the resources used by odo by using the command odo delete component:

  1. odo delete component

Sample Output

  1. $ odo delete component
  2. Searching resources to delete, please wait...
  3. This will delete "my-node-app" from the namespace "odo-dev".
  4. The following resources will get deleted from cluster:
  5. - Deployment: my-node-app
  6. - Service: my-node-app
  7. - Ingress: my-node-app
  8. ? Are you sure you want to delete "my-node-app" and all its resources? Yes
  9. Deleting resources from cluster [52ms]
  10. The component "my-node-app" is successfully deleted from namespace "odo-dev"