<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Philippe MARTIN</title>
    <description>The latest articles on DEV Community by Philippe MARTIN (@feloy).</description>
    <link>https://dev.to/feloy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F178862%2F1b04bfd8-e345-447f-93e3-c31d2450b4a5.jpeg</url>
      <title>DEV Community: Philippe MARTIN</title>
      <link>https://dev.to/feloy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/feloy"/>
    <language>en</language>
    <item>
      <title>odo v3, a new version of odo based on devfiles</title>
      <dc:creator>Philippe MARTIN</dc:creator>
      <pubDate>Mon, 02 May 2022 13:51:52 +0000</pubDate>
      <link>https://dev.to/feloy/odo-v3-a-new-version-of-odo-based-on-devfiles-4c8j</link>
      <guid>https://dev.to/feloy/odo-v3-a-new-version-of-odo-based-on-devfiles-4c8j</guid>
      <description>&lt;p&gt;odo is a tool that aims to simplify the life of developers working on cloud-native applications.&lt;/p&gt;

&lt;p&gt;Thanks to the emergence of the &lt;a href="https://devfile.io/"&gt;devfile&lt;/a&gt; open standard, which  has been accepted as a CNCF Sandbox project recently (in January 2022), odo v3 is now entirely based on this open standard.&lt;/p&gt;

&lt;p&gt;The goal of the devfile standard is to define the structure of applications and how developers can work on them.&lt;/p&gt;

&lt;p&gt;A single devfile defines the smallest building block of an application, that a developer can build, run, test, debug and deploy. In a cloud-native environment, we generally talk about a micro-service. &lt;/p&gt;

&lt;p&gt;Firstly, the devfile describes the container that is needed to be deployed on a cluster during the development phases, along with the commands to execute on this container to build, run, test and debug the program, assuming the sources have been synchronized into the container.&lt;/p&gt;

&lt;p&gt;Secondly, the devfile provides the instructions to build the container image ready for production, along with the Kubernetes resources to deploy to the cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  An example of devfile
&lt;/h2&gt;

&lt;p&gt;To illustrate, here is a simple yet complete devfile, usable for a NodeJS micro-service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;schemaVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2.2.0&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Stack with NodeJS &lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodeJS Runtime&lt;/span&gt;
  &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-nodejs-app&lt;/span&gt;
  &lt;span class="na"&gt;projectType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs&lt;/span&gt;
&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CONTAINER_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;quay.io/phmartin/myimage&lt;/span&gt;
&lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;runtime&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http-3000&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debug-5858&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5858&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.access.redhat.com/ubi8/nodejs-14:latest&lt;/span&gt;
    &lt;span class="na"&gt;memoryLimit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1024Mi&lt;/span&gt;
    &lt;span class="na"&gt;mountSources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;sourceMapping&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/project&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outerloop-build&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;buildContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PROJECT_ROOT}&lt;/span&gt;
      &lt;span class="na"&gt;rootRequired&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./Dockerfile&lt;/span&gt;
    &lt;span class="na"&gt;imageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{CONTAINER_IMAGE}}"&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outerloop-deployment&lt;/span&gt;
  &lt;span class="na"&gt;kubernetes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inlined&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;kind: Deployment&lt;/span&gt;
      &lt;span class="s"&gt;apiVersion: apps/v1&lt;/span&gt;
      &lt;span class="s"&gt;metadata:&lt;/span&gt;
        &lt;span class="s"&gt;name: my-node&lt;/span&gt;
      &lt;span class="s"&gt;spec:&lt;/span&gt;
        &lt;span class="s"&gt;replicas: 1&lt;/span&gt;
        &lt;span class="s"&gt;selector:&lt;/span&gt;
          &lt;span class="s"&gt;matchLabels:&lt;/span&gt;
            &lt;span class="s"&gt;app: node-app&lt;/span&gt;
        &lt;span class="s"&gt;template:&lt;/span&gt;
          &lt;span class="s"&gt;metadata:&lt;/span&gt;
            &lt;span class="s"&gt;labels:&lt;/span&gt;
              &lt;span class="s"&gt;app: node-app&lt;/span&gt;
          &lt;span class="s"&gt;spec:&lt;/span&gt;
            &lt;span class="s"&gt;containers:&lt;/span&gt;
              &lt;span class="s"&gt;- name: my-node&lt;/span&gt;
                &lt;span class="s"&gt;image: {{CONTAINER_IMAGE}}&lt;/span&gt;
                &lt;span class="s"&gt;ports:&lt;/span&gt;
                  &lt;span class="s"&gt;- name: http&lt;/span&gt;
                    &lt;span class="s"&gt;containerPort: 3001&lt;/span&gt;
                    &lt;span class="s"&gt;protocol: TCP&lt;/span&gt;
                &lt;span class="s"&gt;resources:&lt;/span&gt;
                  &lt;span class="s"&gt;limits:&lt;/span&gt;
                    &lt;span class="s"&gt;memory: "1024Mi"&lt;/span&gt;
                    &lt;span class="s"&gt;cpu: "500m"&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outerloop-service&lt;/span&gt;
  &lt;span class="na"&gt;kubernetes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inlined&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;apiVersion: v1&lt;/span&gt;
      &lt;span class="s"&gt;kind: Service&lt;/span&gt;
      &lt;span class="s"&gt;metadata:&lt;/span&gt;
        &lt;span class="s"&gt;name: svc&lt;/span&gt;
      &lt;span class="s"&gt;spec:&lt;/span&gt;
        &lt;span class="s"&gt;ports:&lt;/span&gt;
        &lt;span class="s"&gt;- name: "3000"&lt;/span&gt;
          &lt;span class="s"&gt;port: 3000&lt;/span&gt;
          &lt;span class="s"&gt;protocol: TCP&lt;/span&gt;
          &lt;span class="s"&gt;targetPort: 3000&lt;/span&gt;
        &lt;span class="s"&gt;selector:&lt;/span&gt;
          &lt;span class="s"&gt;app: node-app&lt;/span&gt;
        &lt;span class="s"&gt;type: ClusterIP&lt;/span&gt;
&lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install&lt;/span&gt;
  &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commandLine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;runtime&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;isDefault&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
    &lt;span class="na"&gt;workingDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/project&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;run&lt;/span&gt;
  &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commandLine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm start&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;runtime&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;isDefault&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;run&lt;/span&gt;
    &lt;span class="na"&gt;workingDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/project&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debug&lt;/span&gt;
  &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commandLine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run debug&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;runtime&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;isDefault&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debug&lt;/span&gt;
    &lt;span class="na"&gt;workingDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/project&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commandLine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;runtime&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;isDefault&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;workingDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/project&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;composite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build-image&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;k8s-deployment&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;k8s-service&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;isDefault&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-image&lt;/span&gt;
  &lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outerloop-build&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;k8s-deployment&lt;/span&gt;
  &lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outerloop-deployment&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;k8s-service&lt;/span&gt;
  &lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outerloop-service&lt;/span&gt;
&lt;span class="na"&gt;starterProjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs-starter&lt;/span&gt;
  &lt;span class="na"&gt;git&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;remotes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/odo-devfiles/nodejs-ex.git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;runtime&lt;/code&gt; component defines the container that will be deployed to support the program in development. Specifically, it will use the image &lt;code&gt;registry.access.redhat.com/ubi8/nodejs-14:latest&lt;/code&gt;, and sources should be placed in the &lt;code&gt;/project&lt;/code&gt; directory of the container. Two endpoints are also defined, one to access the micro-service, the other to help the debugger attach to the process, during debugging sessions.&lt;/p&gt;

&lt;p&gt;The commands &lt;code&gt;install&lt;/code&gt;, &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;debug&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; indicate which commands to execute to respectively build, execute, debug and test the application. For example, the &lt;code&gt;npm install&lt;/code&gt; command will be executed in the container to build the application, then &lt;code&gt;npm start&lt;/code&gt; will be executed to start the application.&lt;/p&gt;

&lt;p&gt;To deploy the micro-service, the component &lt;code&gt;outerloop-build&lt;/code&gt; indicates how to build the production image (by using &lt;code&gt;./Dockerfile&lt;/code&gt;, and creating an image whose name is defined by the variable &lt;code&gt;CONTAINER_IMAGE&lt;/code&gt; defined at the beginning of the devfile). then, two other components &lt;code&gt;outerloop-deployment&lt;/code&gt; and &lt;code&gt;outerloop-service&lt;/code&gt; define the Kubernetes resources to deploy to the cluster. Note that the first one defines a Deployment that will help deploy a container using the image built with the previous &lt;code&gt;outerloop-build&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;starterProjects&lt;/code&gt; section at the end of the devfile indicates a list of starter projects, that can be downloaded to have an example of program deployable with this devfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Devfile registry
&lt;/h2&gt;

&lt;p&gt;We can see through the previous example that a devfile is generic enough, with only a few specific values, like the endpoints and the image names. A devfile written for a specific language and framework can be used by most of the programs written using this language and framework, with minimum personalization.&lt;/p&gt;

&lt;p&gt;A devfile registry is available at &lt;a href="https://registry.devfile.io"&gt;https://registry.devfile.io&lt;/a&gt;, containing devfiles for a large variety of languages and frameworks, and you can deploy your own registry to make accessible your own devfiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing odo v3
&lt;/h2&gt;

&lt;p&gt;You can find the instructions to install odo v3-alpha1 from &lt;a href="https://github.com/redhat-developer/odo/releases/tag/v3.0.0-alpha1"&gt;this release page&lt;/a&gt;. The binaries are accessible &lt;a href="https://developers.redhat.com/content-gateway/rest/mirror/pub/openshift-v4/clients/odo/v3.0.0~alpha1/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initializing a project
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;odo init&lt;/code&gt; command is the first command to use, before to start using &lt;code&gt;odo&lt;/code&gt; with your project. The goal of this first step is to get a suitable devfile for your project.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;odo init&lt;/code&gt; will search for devfiles into devfile registries. By default, &lt;code&gt;odo&lt;/code&gt; is configured to access only one devfile registry (the one specified above), and you can modify the devfile registries &lt;code&gt;odo&lt;/code&gt; is accessing using the command &lt;code&gt;odo preference registry&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;odo init&lt;/code&gt; command offers two modes, either interactive, or manual. The interactive mode will help you discover the appropriate devfile. To use the interactive mode, you just need to enter &lt;code&gt;odo init&lt;/code&gt; in your command line.&lt;/p&gt;

&lt;p&gt;If you execute this command from a directory containing sources, &lt;code&gt;odo&lt;/code&gt; will try to recognize the language and framework you are using, will search into the devfile registries you have configured the most appropriate devfile, and give you the choice to use this one, or to search for another one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo init
  __
 /  \__     Initializing new component
 \__/  \    Files: Source code detected, a Devfile will be determined based upon source code autodetection
 /  \__/    odo version: v3.0.0-alpha1
 \__/

Interactive mode enabled, please answer the following questions:
Based on the files in the current directory odo detected
Language: javascript
Project type: nodejs
The devfile "nodejs" from the registry "DefaultDevfileRegistry" will be downloaded.
? Is this correct? (Y/n) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you answer &lt;code&gt;No&lt;/code&gt; here, or if you run the &lt;code&gt;odo init&lt;/code&gt; command from an empty directory, &lt;code&gt;odo init&lt;/code&gt; will help you choose the appropriate devfile. The command will also help you make some personalization on the devfile, by personalizing the endpoints and the environment variables for the container that will be deployed during the development phase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Select language: javascript
? Select project type: Node.js Runtime
 ✓  Downloading devfile "nodejs" from registry "DefaultDevfileRegistry" [961ms]
Current component configuration:
Container "runtime":
  Opened ports:
   - 3000
  Environment variables:
? Select container for which you want to change configuration? NONE - configuration is correct
? Enter component name: my-nodejs-app

Your new component 'my-nodejs-app' is ready in the current directory.
To start editing your component, use 'odo dev' and open this folder in your favorite IDE.
Changes will be directly reflected on the cluster.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, if you start the &lt;code&gt;odo init&lt;/code&gt; command from an empty directory, it will give you the choice to download one of the starter projects listed in the devfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  The development phase
&lt;/h2&gt;

&lt;p&gt;Now that a devfile is present in the current directory, you can run your application in development mode, using the &lt;code&gt;odo dev&lt;/code&gt; command. This command will create a &lt;code&gt;Deployment&lt;/code&gt; in the cluster that will help start a container as defined in the devfile. Then, the sources present in the current directory will be synchronized into the container, and the commands to build and run the application will be exectued from inside the container.&lt;/p&gt;

&lt;p&gt;At the same time, a port-forwarding will be done for each endpoint defined in the devfile, so you can access the container ports through local ports in your development machine.&lt;/p&gt;

&lt;p&gt;Finally, &lt;code&gt;odo&lt;/code&gt; will watch for changes in the current directory. When files are modified, added or deleted, &lt;code&gt;odo&lt;/code&gt; will synchronize the changes to the container, and will restart the build and run commands from inside the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo dev
  __
 /  \__     Developing using the my-nodejs-app Devfile
 \__/  \    Namespace: prj2
 /  \__/    odo version: v3.0.0-alpha1
 \__/

↪ Deploying to the cluster in developer mode
 ✓  Waiting for Kubernetes resources [6s]
 ✓  Syncing files into the container [439ms]
 ✓  Building your application in container on cluster [3s]
 ✓  Executing the application [1s]

Your application is now running on the cluster
 - Forwarding from 127.0.0.1:40001 -&amp;gt; 3000
 - Forwarding from 127.0.0.1:40002 -&amp;gt; 5858

Watching for changes in the current directory /home/phmartin/Documents/tests/devto-deploy
Press Ctrl+c to exit `odo dev` and delete resources from the cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to debug the application, you will need to run the &lt;code&gt;odo dev --debug&lt;/code&gt; command instead.&lt;/p&gt;

&lt;p&gt;When you have finished the development session, you just need to hit Ctrl-c to stop the &lt;code&gt;odo dev&lt;/code&gt; command. The command won't terminate immediately, as it will delete the resources it has deployed on the cluster before to exit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The deployment phase
&lt;/h2&gt;

&lt;p&gt;When you are satisfied with your program, you may want to deploy it by building the container image using a &lt;code&gt;Dockerfile&lt;/code&gt;, instead of using a generic image as during the development phase, and by deploying into the cluster personalized resources, instead of the Deployment used during the development phase.&lt;/p&gt;

&lt;p&gt;At the time of writing, no devfile of the default devfile registry contains instructions for the deployment phase. By using the devfile provided as example above, the command &lt;code&gt;odo deploy&lt;/code&gt; will deploy a personalized Deployment and a Service into the cluster, after building the container image using the &lt;code&gt;Dockerfile&lt;/code&gt; present in the directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo deploy
  __
 /  \__     Deploying the application using my-nodejs-app Devfile
 \__/  \    Namespace: prj2
 /  \__/    odo version: v3.0.0-alpha1
 \__/

↪ Building &amp;amp; Pushing Container: quay.io/phmartin/myimage
 •  Building image locally  ...
STEP 1/7: FROM docker.io/library/node:17
STEP 2/7: WORKDIR /usr/src/app
[...]
STEP 7/7: CMD [ "node", "server.js" ]
COMMIT quay.io/phmartin/myimage
 ✓  Building image locally [6s]
 •  Pushing image to container registry  ...
[...]
Writing manifest to image destination
Storing signatures
 ✓  Pushing image to container registry [8s]

↪ Deploying Kubernetes Component: my-node
 ✓  Searching resource in cluster 
 ✓  Creating kind Deployment [50ms]

↪ Deploying Kubernetes Component: svc
 ✓  Searching resource in cluster 
 ✓  Creating kind Service [57ms]

Your Devfile has been successfully deployed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At any moment, you can check if a component has been deployed, using the &lt;code&gt;odo list&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo list
 ✓  Listing components from namespace 'prj2' [61ms]
 NAME             PROJECT TYPE  RUNNING IN  MANAGED 
 * my-nodejs-app  nodejs        Deploy      odo     
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you are done with this application, or if you want to undeploy it to work on development mode again, you can use the &lt;code&gt;odo delete component&lt;/code&gt; to undeploy the component from the cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo delete component
Searching resources to delete, please wait...
This will delete "my-nodejs-app" from the namespace "prj2".
 •  The component contains the following resources that will get deleted:
    - Deployment: my-node
    - Service: svc
? Are you sure you want to delete "my-nodejs-app" and all its resources? Yes
The component "my-nodejs-app" is successfully deleted from namespace "prj2"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Binding a service to a micro-service using odo</title>
      <dc:creator>Philippe MARTIN</dc:creator>
      <pubDate>Sun, 05 Sep 2021 16:02:31 +0000</pubDate>
      <link>https://dev.to/feloy/binding-a-service-to-a-micro-service-using-odo-462k</link>
      <guid>https://dev.to/feloy/binding-a-service-to-a-micro-service-using-odo-462k</guid>
      <description>&lt;p&gt;In this article, we will see how to bind a service to a micro-service, using the odo development tool.&lt;/p&gt;

&lt;p&gt;We will first see how the Service Binding Operator can be used to bind to a PostgreSQL instance started from a standalone Deployment, then to a PostgreSQL instance started from an Operator.&lt;/p&gt;

&lt;p&gt;Next, we will see how to start the Operator backed PostgreSQL service from odo, and how to bind our micro-service to this PostgreSQL service using odo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Service Binding Operator
&lt;/h2&gt;

&lt;p&gt;The Service Binding Operator is an operator that helps the developer get the values of the different parameters exposed by a service.&lt;/p&gt;

&lt;p&gt;For example, when you have a PostgreSQL service deployed, you need to know its host, database name, user and password to work with the service. If the service provides which information are to be exposed, the Service Binding Operator is able to get the exposed values and provide them to your micro-service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing the Service Binding Operator
&lt;/h3&gt;

&lt;p&gt;You can install this operator with the help of the Operator Lifecycle Manager. For this, first install, if necessary, this manager with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.18.3/install.sh | bash -s v0.18.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, install the Service Binding Operator with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl create -f https://operatorhub.io/install/service-binding-operator.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploying a standalone PostgreSQL instance
&lt;/h2&gt;

&lt;p&gt;Let's deploy a single PostgreSQL instance using the official &lt;code&gt;postgres&lt;/code&gt; image, available at &lt;a href="https://hub.docker.com/_/postgres"&gt;https://hub.docker.com/_/postgres&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can deploy an instance by defining our own database name, user and password with the following manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: postgres
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - image: postgres
        name: postgres
        env:
        - name: POSTGRES_PASSWORD
          value: a-super-secret
        - name: POSTGRES_USER
          value: user1
        - name: POSTGRES_DB
          value: db1
        volumeMounts:
        - mountPath: /var/lib/postgresql/data
          name: pgdata
      volumes:
      - name: pgdata
        emptyDir: {}

---

apiVersion: v1
kind: Service
metadata:
  name: postgres-svc
spec:
  selector:
    app: postgres
  ports:
  - port: 5432
    targetPort: 5432
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the service is running, you can run another container that will be ready to connect to it using &lt;code&gt;psql&lt;/code&gt;, with the manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: client
spec:
  selector:
    matchLabels:
      app: client
  template:
    metadata:
      labels:
        app: client
    spec:
      containers:
      - name: client
        image: postgres
        command: ["bash", "-c", "sleep $((10**10))"]
        env:
        - name: PGPASSWORD
          value: a-super-secret
        - name: PGHOST
          value: postgres-svc
        - name: PGDATABASE
          value: db1
        - name: PGUSER
          value: user1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we have defined the environment variables supported by &lt;code&gt;psql&lt;/code&gt; to define the host, database name, username and password.&lt;/p&gt;

&lt;p&gt;You finally can connect to the database with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl exec -it deployment/client psql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The environment variables defined into the manifest for the container will be used by &lt;code&gt;psql&lt;/code&gt; to connect to the desired service.&lt;/p&gt;

&lt;p&gt;But with this method, the developer needs to know the values for these different parameters, when running the client. Let's see how these variables can be automatically injected into the pod, using the Service Binding Operator.&lt;/p&gt;

&lt;h3&gt;
  
  
  Annotating the service resource
&lt;/h3&gt;

&lt;p&gt;The first step is to annotate the &lt;code&gt;postgres&lt;/code&gt; Deployment, to indicate which parameters should be exposed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    service.binding/pguser: path={.spec.template.spec.containers[0].env[?(@.name=="POSTGRES_USER")].value}
    service.binding/pgpassword: path={.spec.template.spec.containers[0].env[?(@.name=="POSTGRES_PASSWORD")].value}
    service.binding/pgdatabase: path={.spec.template.spec.containers[0].env[?(@.name=="POSTGRES_DB")].value}
  labels:
    app: postgres
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - image: postgres
        name: postgres
        env:
        - name: POSTGRES_PASSWORD
          value: a-super-secret
        - name: POSTGRES_USER
          value: user1
        - name: POSTGRES_DB
          value: db1
        volumeMounts:
          - mountPath: /var/lib/postgresql/data
            name: pgdata
      volumes:
      - name: pgdata
        emptyDir: {}

---

apiVersion: v1
kind: Service
metadata:
  name: postgres-svc
spec:
  selector:
    app: postgres
  ports:
  - port: 5432
    targetPort: 5432
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Linking to the annotated service
&lt;/h3&gt;

&lt;p&gt;Then, you can create a &lt;code&gt;client2&lt;/code&gt; deployment, which does not define the database name, user and password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: client2
spec:
  selector:
    matchLabels:
      app: client2
  template:
    metadata:
      labels:
        app: client2
    spec:
      containers:
      - name: client2
        image: postgres
        command: ["bash", "-c", "sleep $((10**10))"]
        env:
        - name: PGHOST
          value: postgres-svc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you can create a &lt;code&gt;servicebinding&lt;/code&gt; resource, to define a link between the Postgres service and the client2 deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: binding.operators.coreos.com/v1alpha1
kind: ServiceBinding
metadata:
  name: binding-request
spec:
  bindAsFiles: false
  namingStrategy: '{{ .name | upper }}'
  application:
    name: client2
    group: apps
    version: v1
    resource: deployments
  services:
  - group: apps
    version: v1
    kind: Deployment
    name: postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By applying this manifest, the &lt;code&gt;client2&lt;/code&gt; pod will be restarted, and environment variables will be injected through a &lt;code&gt;secretRef&lt;/code&gt;, by the Service Binding Operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get deployment client2 -o yaml
[...]
spec:
  containers:
  - command:
    - bash
    - -c
    - sleep $((10**10))
    env:
    - name: PGHOST
      value: postgres-svc
    envFrom:
    - secretRef:
        name: binding-request-703eda68

$ kubectl get secret binding-request-703eda68 -o yaml
apiVersion: v1
kind: Secret
metadata:
  name: binding-request-703eda68
data:
  PGDATABASE: ZGIx
  PGPASSWORD: YS1zdXBlci1zZWNyZXQ=
  PGUSER: dXNlcjE=
type: Opaque
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can connect to the service from the &lt;code&gt;client2&lt;/code&gt; deployment, without the need to know the credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying an Operator backed PostgreSQL service
&lt;/h2&gt;

&lt;p&gt;Operator backed services are services managed by a Kubernetes operator. &lt;/p&gt;

&lt;p&gt;You can find such Operator backed services on &lt;a href="https://operatorhub.io/"&gt;https://operatorhub.io/&lt;/a&gt;. For this example, we won't use operatorhub, but deploy manually the operator found at &lt;a href="https://github.com/operator-backing-service-samples/postgresql-operator"&gt;https://github.com/operator-backing-service-samples/postgresql-operator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To deploy the PostgreSQL operator using the OLM framework, we first need to declare the catalog containing the Operator, then subscribe to this specific Operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl apply -f - &amp;lt;&amp;lt; EOD
--------
apiVersion: operators.coreos.com/v1alpha1
kind: CatalogSource
metadata:
    name: sample-db-operators
    namespace: olm
spec:
    sourceType: grpc
    image: quay.io/redhat-developer/sample-db-operators-olm:v1
    displayName: Sample DB Operators

--------

apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  name: pg
  namespace: operators
spec:
  channel: stable
  name: db-operators
  source: sample-db-operators
  sourceNamespace: olm
  installPlanApproval: Automatic
EOD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Examining the annotations on the Database custom resource
&lt;/h3&gt;

&lt;p&gt;The PostgreSQL operator comes with a custom resource definition (CRD): &lt;code&gt;Database&lt;/code&gt;. Let's examine the definition of this resource with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get crd databases.postgresql.baiju.dev -o yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.postgresql.baiju.dev
  annotations:
    service.binding/db.host: path={.status.dbConfigMap},objectType=ConfigMap
    service.binding/db.name: path={.status.dbConfigMap},objectType=ConfigMap
    service.binding/db.password: path={.status.dbConfigMap},objectType=ConfigMap
    service.binding/db.port: path={.status.dbConfigMap},objectType=ConfigMap
    service.binding/db.user: path={.status.dbConfigMap},objectType=ConfigMap
    service.binding/dbConnectionIP: path={.status.dbConnectionIP}
    service.binding/dbConnectionPort: path={.status.dbConnectionPort}
    service.binding/dbName: path={.spec.dbName}
    service.binding/password: path={.status.dbCredentials},objectType=Secret
    service.binding/user: path={.status.dbCredentials},objectType=Secret
[...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can find that the CRD get a list of &lt;code&gt;service.binding&lt;/code&gt; annotations, that will be used by the Service Binding Operator to bind instances of this resource to micro-services.&lt;/p&gt;

&lt;p&gt;Note that, earlier in this article, annotations were placed in the &lt;code&gt;Deployment&lt;/code&gt; &lt;strong&gt;instance&lt;/strong&gt; itself; annotations are now placed in the &lt;strong&gt;definition&lt;/strong&gt; of the &lt;code&gt;Database&lt;/code&gt; resource (if you are an object-oriented developer, think about Classes and Instances in place of Definition and Instance).&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Database instance with odo
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;odo&lt;/code&gt; tool can create instances of Operator backend services.&lt;/p&gt;

&lt;p&gt;You can get the list of available Operators on your cluster with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo catalog list services
Services available through Operators
NAME                                CRDs
postgresql-operator.v0.0.9          Database
service-binding-operator.v0.9.1     ServiceBinding, ServiceBinding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that this command displays the list of &lt;code&gt;ClusterServiceVersions&lt;/code&gt; resources contained in the current namespace in the &lt;code&gt;Succeeded&lt;/code&gt; phase. The &lt;code&gt;Succeeded&lt;/code&gt; phase can take a few minutes to appear, please be patient; you can check with the command:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get csv 
NAME                              DISPLAY                    VERSION   REPLACES                          PHASE
postgresql-operator.v0.0.9        PostgreSQL Database        0.0.9                                       Succeeded
service-binding-operator.v0.9.1   Service Binding Operator   0.9.1     service-binding-operator.v0.9.0   Succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With odo, services are attached to components; before to deploy a service, you need to create a component. You can follow this article to create your component, and get an example of sources in the repository &lt;a href="https://github.com/feloy/nest-odo-example"&gt;https://github.com/feloy/nest-odo-example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, you can create an instance of a PostgreSQL database with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo service create postgresql-operator.v0.0.9/Database db1
Successfully added service to the configuration; do 'odo push' to create service on the cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create an instance with the default parameters. If you want to use a specific configuration, you can get the possible parameters with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo catalog describe service postgresql-operator.v0.0.9/Database
Kind: Database
Version: v1alpha1
Description: Describes how an application component is built and deployed.
Parameters:

 PATH       DISPLAYNAME                     DESCRIPTION                    

 image      PostgreSQL database image       PostgreSQL database image      
 imageName  PostgreSQL database image name  PostgreSQL database image name 
 dbName     DB name                         Desired database name. If      
                                            not provided, default value    
                                            'postgres' will be used.       
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now create an instance with a specific database name with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo service create postgresql-operator.v0.0.9/Database db1 -p dbName=mydb -p imageName=postgresql -p image=postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you can deploy the service into the Kubernetes cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;odo push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Binding the service with odo
&lt;/h3&gt;

&lt;p&gt;This odo command will create the Service Binding resource necessary to bind the PostgreSQL service to your micro-service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo link Database/db1 --name dblink
 ✓  Successfully created link between component "nodejs-prj1-api-hjtd" and service "Database/db1"

$ odo push

$ kubectl get sbr dblink -o yaml 
kind: ServiceBinding
metadata:
  name: dblink
  [...]
spec:
  application:
    group: apps
    name: nodejs-prj1-api-hjtd-app
    resource: deployments
    version: v1
  bindAsFiles: false
  detectBindingResources: true
  services:
  - group: postgresql.baiju.dev
    kind: Database
    name: db1
    version: v1alpha1
[...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can examine that the environment variables have been defined into your micro-service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo exec -- bash -c export | grep DATABASE_
declare -x DATABASE_CLUSTERIP="10.107.65.179"
declare -x DATABASE_DBCONNECTIONIP="10.107.65.179"
declare -x DATABASE_DBCONNECTIONPORT="5432"
declare -x DATABASE_DBNAME="mydb"
declare -x DATABASE_PASSWORD="password"
declare -x DATABASE_USER="postgres"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developer can now use these variables into the code of the micro-service, and does not need to declare the values for these variables when deploying the micro-service, as the injection of the environment variables will be managed by the Service Binding Operator.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>development</category>
      <category>odo</category>
      <category>binding</category>
    </item>
    <item>
      <title>odo, a tool to simplify development on Kubernetes</title>
      <dc:creator>Philippe MARTIN</dc:creator>
      <pubDate>Mon, 10 May 2021 08:00:35 +0000</pubDate>
      <link>https://dev.to/feloy/odo-a-tool-to-simplify-development-on-kubernetes-5c9a</link>
      <guid>https://dev.to/feloy/odo-a-tool-to-simplify-development-on-kubernetes-5c9a</guid>
      <description>&lt;p&gt;In this article, we will see how to use the &lt;code&gt;odo&lt;/code&gt; tool to develop cloud native applications, without the need for developers to manipulate containers and Kubernetes resources.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;odo&lt;/code&gt; tool is based on the deployment of pre-defined containers embedding all the environment to build and execute the developed programs. This way, the developers do not need to write any &lt;code&gt;Dockerfile&lt;/code&gt; or Kubernetes manifests. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In the article, we will use quoted text, as for this paragraph, and a different narrative, to describe what happens in the Kubernetes cluster, generally using the &lt;code&gt;kubectl&lt;/code&gt; command. This helps to clearly separate what are the commands executed by developers on a daily basis, and what are the commands executed only to illustrate in the article what happens when developers execute the commands, and what an experienced Kubernetes user can expect to see in the cluster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Installing &lt;code&gt;odo&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;odo&lt;/code&gt; tool is composed of a single binary. Developers can install the binary either from the Golang source available at github.com/openshift/odo, or can download a binary from &lt;a href="https://mirror.openshift.com/pub/openshift-v4/clients/odo/"&gt;https://mirror.openshift.com/pub/openshift-v4/clients/odo/&lt;/a&gt;. The detailed instructions to install &lt;code&gt;odo&lt;/code&gt; are available at &lt;a href="https://odo.dev/docs/installing-odo/"&gt;https://odo.dev/docs/installing-odo/&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto-completion
&lt;/h3&gt;

&lt;p&gt;Developers can activate auto-completion for the &lt;code&gt;odo&lt;/code&gt; command using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo --complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;On Linux, this command will add the following line at your &lt;code&gt;~/.bashrc&lt;/code&gt; file:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;complete -C /path/to/your/odo odo
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;To effectively activate the completion without restarting a new terminal, developers need to reload the &lt;code&gt;.bashrc&lt;/code&gt; file, with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a new project
&lt;/h2&gt;

&lt;p&gt;An application developed with &lt;code&gt;odo&lt;/code&gt; is contained into a &lt;strong&gt;project&lt;/strong&gt;. Developers can create a new project with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo project create prj1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;On the cluster, this command creates a Kubernetes namespace of the same name into the current cluster:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get namespaces
NAME              STATUS   AGE
[...]
prj1              Active   5s
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;It also sets this newly created namespace as the current namespace of the current Kubernetes context:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl ns -c
prj1
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Creating a new micro-service
&lt;/h2&gt;

&lt;p&gt;As an example of micro-service, we will create an API, using the Nest framework:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nest new prj1-api
$ cd prj1-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can test the API locally, by running the following commands:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm install
$ npm start
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Executing these commands, and if you are lucky enough (if the port 3000 is not already in use for example), the API will start listening for HTTP requests on the port 3000 of your host.&lt;/p&gt;

&lt;p&gt;You can test the result of a request with the command from a new terminal:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl http://localhost:3000
Hello World!
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deploying the micro-service
&lt;/h2&gt;

&lt;p&gt;Instead of building and running the API locally, developers can deploy the API into the Kubernetes cluster using specific &lt;code&gt;odo&lt;/code&gt; commands.&lt;/p&gt;

&lt;p&gt;First, developers need to add the API as a component of the project, with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo create nodejs prj1-api
Devfile Object Validation
 ✓  Checking devfile existence [51789ns]
 ✓  Creating a devfile component from registry: DefaultDevfileRegistry [82276ns]
Validation
 ✓  Validating if devfile name is correct [93222ns]

Please use `odo push` command to create the component with source deployed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within this command, developers indicate that the component uses the &lt;code&gt;nodejs&lt;/code&gt; environment. This way, &lt;code&gt;odo&lt;/code&gt; knows it has to deploy a pre-defined container containing all the tools necessary to build and execute such applications (for example &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;node&lt;/code&gt;). &lt;code&gt;odo&lt;/code&gt; also knows how to build and start the application (here with &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;npm start&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The effect of this command is to create a &lt;code&gt;devfile.yaml&lt;/code&gt; file, describing the project and its components. At this point, nothing has been deployed into the cluster, and the developer has to run the following command to deploy the component just defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo push
Validation
 ✓  Validating the devfile [20769ns]

Creating Kubernetes resources for component prj1-api
 ✓  Waiting for component to start [2m]
 ✓  Waiting for component to start [4ms]
 ⚠  Unable to create ingress, missing host information for Endpoint http-3000, please check instructions on URL creation (refer `odo url create --help`)


Applying URL changes
 ✓  URLs are synced with the cluster, no changes are required.

Syncing to component prj1-api
 ✓  Checking files for pushing [453118ns]
 ✓  Syncing files to the component [113ms]

Executing devfile commands for component prj1-api
 ✓  Waiting for component to start [1ms]
 ✓  Executing install command "npm install" [32s]
 ✓  Executing run command "npm start" [1s]

Pushing devfile component "prj1-api"
 ✓  Changes successfully pushed to component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;On the cluster, a pod will be deployed, containing a container in which the API will be built and started.&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
prj1-api-7d58fcf4f4-4m727   1/1     Running   0          2m41s
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;A Kubernetes &lt;em&gt;Service&lt;/em&gt; has been created, and you can try to access the API through the service address:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl port-forward svc/prj1-api 3000 &amp;gt; /dev/null &amp;amp;
$  curl http://localhost:3000/
Hello World!
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;Developers can examine the logs of the component with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo log
[...]
[Nest] 108   - 05/07/2021, 11:16:06 AM   [NestApplication] Nest application successfully started
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Working on the micro-service
&lt;/h2&gt;

&lt;p&gt;After editing the code of the API, developers can restart the API into the cluster with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will synchronize the sources into the container, and will rebuild and restart the application in the container.&lt;/p&gt;

&lt;p&gt;If developers want to automate this process, they can run the command &lt;code&gt;odo watch&lt;/code&gt;, and this process will be done every time they save modifications into the API sources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If developers want to undeploy the API from the cluster, keeping the information into the &lt;code&gt;devfile.yaml&lt;/code&gt; file, they can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo delete prj1-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Debugging the micro-service
&lt;/h2&gt;

&lt;p&gt;To debug the process executed into the container, the developer can run &lt;code&gt;odo push&lt;/code&gt; with the &lt;code&gt;--debug&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;First, the default &lt;code&gt;package.json&lt;/code&gt; file created by Nest must be modified to adapt to the &lt;code&gt;devfile.yaml&lt;/code&gt; and to &lt;code&gt;odo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Looking at the &lt;code&gt;debug&lt;/code&gt; command in the &lt;code&gt;devfile.yml&lt;/code&gt; file, we can see that the command to be executed by &lt;code&gt;odo&lt;/code&gt; to run the process in debug mode is &lt;code&gt;npm run debug&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commands:
- id: debug
  exec:
    component: runtime
    commandLine: npm run debug
    workingDir: /project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, it is good to know that &lt;code&gt;odo&lt;/code&gt; will try to access the debugger inside the container on the port &lt;code&gt;5858&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's adapt the &lt;code&gt;package.json&lt;/code&gt; file to define the command &lt;code&gt;debug&lt;/code&gt; instead of &lt;code&gt;start:debug&lt;/code&gt;, and specify the debug port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  [...]
  "scripts": {
    "debug": "nest start --debug 5858 --watch",
    [...]
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developer can now run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo push --debug
$ odo debug port-forward
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using vscode, the debugger can be launched with this configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "version": "0.2.0",
  "configurations": [ { 
    "name": "Attach to remote 5858",
    "type": "node",
    "request": "attach",
    "address": "localhost",
    "port": 5858,
    "localRoot": "${workspaceFolder}",
    "remoteRoot": "/project"
  } ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One final step is to build the micro-service locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developer is now ready to debug the application from vscode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Micro-services generally need some configuration, and this configuration is generally done through environment variables.&lt;/p&gt;

&lt;p&gt;To pass environment variables to a component, developers can use the &lt;code&gt;odo config set --env&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ odo config set --env PORT=3000 --env LOG_LEVEL=debug
 ✓  Environment variables were successfully updated

Run `odo push` command to apply changes to the cluster

$ odo push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this example, two environment variables &lt;code&gt;PORT&lt;/code&gt; and &lt;code&gt;LOG_LEVEL&lt;/code&gt; will be defined into the container running the API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;These environment variables are defined in the &lt;em&gt;Pod&lt;/em&gt; spec template, as we can see by looking at the definition of the &lt;em&gt;Deployment&lt;/em&gt; resource created by &lt;code&gt;odo&lt;/code&gt;:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ kubectl get deployments.apps prj1-api -o yaml
   [...]
   containers:
   - name: runtime
     env:
     - name: PORT
       value: "3000"
     - name: LOG_LEVEL
       value: debug
   [...]        
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we have introduced the &lt;code&gt;odo&lt;/code&gt; tool and how developers can use it to develop and test their micro-services in a Kubernetes cluster without the need to write &lt;code&gt;Dockerfile&lt;/code&gt; or Kubernetes manifests.&lt;/p&gt;

&lt;p&gt;The complete documentation of &lt;code&gt;odo&lt;/code&gt; is available at &lt;a href="https://odo.dev/"&gt;https://odo.dev/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;odo&lt;/code&gt; tool permits many more things, and also provides IDEs integrations, for &lt;em&gt;Visual Studio Code&lt;/em&gt; and &lt;em&gt;IntelliJ&lt;/em&gt;. More articles are coming with these features.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>development</category>
      <category>odo</category>
    </item>
    <item>
      <title>Deploying an i18n Angular app with angular-cli</title>
      <dc:creator>Philippe MARTIN</dc:creator>
      <pubDate>Sat, 29 Jun 2019 16:48:56 +0000</pubDate>
      <link>https://dev.to/angular/deploying-an-i18n-angular-app-with-angular-cli-2fb9</link>
      <guid>https://dev.to/angular/deploying-an-i18n-angular-app-with-angular-cli-2fb9</guid>
      <description>&lt;p&gt;I will explain in this article how to create from scratch an internationalized (i18n) Angular app with the use of the Angular CLI and how to deploy it on an Apache or NGINX web server.&lt;/p&gt;

&lt;p&gt;The following versions are used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;angular-cli: 8.0.4&lt;/li&gt;
&lt;li&gt;angular: 8.0.4&lt;/li&gt;
&lt;li&gt;Apache 2.4&lt;/li&gt;
&lt;li&gt;NGINX 1.17&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The described sample app is available at: &lt;a href="https://github.com/feloy/angular-cli-i18n-sample" rel="noopener noreferrer"&gt;https://github.com/feloy/angular-cli-i18n-sample&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A fresh i18n app
&lt;/h2&gt;

&lt;p&gt;We first create a fresh Angular app with the help of the Angular CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ng new angular-cli-i18n-sample
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We make some changes to add some translatable text, in &lt;br&gt;
&lt;code&gt;app.component.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1 i18n&amp;gt;Hello world!&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need now to create an xlf file with the translatable strings. We can generate the file &lt;code&gt;src/i18n/messages.xlf&lt;/code&gt; with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ng xi18n --output-path src/i18n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now create translations for different languages, here in english with a fresh file &lt;code&gt;src/i18n/messages.en.xlf&lt;/code&gt; copied from &lt;code&gt;src/i18n/messages.xlf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[...]
      &amp;lt;trans-unit id="[...]" datatype="html"&amp;gt;
        &amp;lt;source&amp;gt;Hello World!&amp;lt;/source&amp;gt;
        &amp;lt;target&amp;gt;Hello World!&amp;lt;/target&amp;gt;
        [...]
      &amp;lt;/trans-unit&amp;gt;
[...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in french with &lt;code&gt;src/i18n/messages.fr.xlf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[...]
      &amp;lt;trans-unit id="[...]" datatype="html"&amp;gt;
        &amp;lt;source&amp;gt;Hello World!&amp;lt;/source&amp;gt;
        &amp;lt;target&amp;gt;Salut la foule !&amp;lt;/target&amp;gt;
        [...]
      &amp;lt;/trans-unit&amp;gt;
[...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and in spanish with &lt;code&gt;src/i18n/messages.es.xlf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[...]
      &amp;lt;trans-unit id="[...]" datatype="html"&amp;gt;
        &amp;lt;source&amp;gt;Hello World!&amp;lt;/source&amp;gt;
        &amp;lt;target&amp;gt;¿hola, qué tal?&amp;lt;/target&amp;gt;
        [...]
      &amp;lt;/trans-unit&amp;gt;
[...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is now possible to make Angular CLI build the app with the language of your choice, here in spanish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ng build --aot \
           --i18n-file=src/i18n/messages.es.xlf \
           --i18n-locale=es \
           --i18n-format=xlf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prepare the app for production
&lt;/h2&gt;

&lt;p&gt;In production, we would like the app to be accessible in different subdirectories, depending on the language; for example the spanish version would be accessible at &lt;a href="http://myapp.com/es/" rel="noopener noreferrer"&gt;http://myapp.com/es/&lt;/a&gt; and the french one at &lt;a href="http://myapp.com/fr/" rel="noopener noreferrer"&gt;http://myapp.com/fr/&lt;/a&gt;. We also would like to be redirected from the base url &lt;a href="http://myapp.com/" rel="noopener noreferrer"&gt;http://myapp.com/&lt;/a&gt; to the url of our preferred language.&lt;/p&gt;

&lt;p&gt;For this, we guess that we need to change the base href to &lt;strong&gt;es&lt;/strong&gt;, &lt;strong&gt;en&lt;/strong&gt; or &lt;strong&gt;fr&lt;/strong&gt;, depending on the target language. Angular CLI has a special command-line option for this, &lt;code&gt;--base-href&lt;/code&gt; which permits to declare the base href at compile time from command line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux/macOS users
&lt;/h3&gt;

&lt;p&gt;Here is the shell command we can use to create the different bundles for the different languages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ for lang in es en fr; do \
    ng build --output-path=dist/$lang \
             --aot \
             --prod \
             --base-href /$lang/ \
             --i18n-file=src/i18n/messages.$lang.xlf \
             --i18n-format=xlf \
             --i18n-locale=$lang; \
  done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can create a script definition in package.json for this command and execute it with npm run build-i18n:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  [...]
  "scripts": {
    [...]
    "build-i18n": "for lang in en es fr; do ng build --output-path=dist/$lang --aot --prod --base-href /$lang/ --i18n-file=src/i18n/messages.$lang.xlf --i18n-format=xlf --i18n-locale=$lang; done"
  }
  [...]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we get three directories &lt;code&gt;en/&lt;/code&gt;, &lt;code&gt;es/&lt;/code&gt; and &lt;code&gt;fr/&lt;/code&gt; into the &lt;code&gt;dist/&lt;/code&gt; directory, containing the different bundles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows users
&lt;/h3&gt;

&lt;p&gt;As a Windows user, you can use these commands to build your different bundles for different languages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; ng build --output-path=dist/fr --aot --prod --base-href /fr/ --i18n-file=src/i18n/messages.fr.xlf --i18n-format=xlf --i18n-locale=fr

&amp;gt; ng build --output-path=dist/es --aot --prod --base-href /es/ --i18n-file=src/i18n/messages.es.xlf --i18n-format=xlf --i18n-locale=es

&amp;gt; ng build --output-path=dist/en --aot --prod --base-href /en/ --i18n-file=src/i18n/messages.en.xlf --i18n-format=xlf --i18n-locale=en
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can create script definitions in &lt;code&gt;package.json&lt;/code&gt; for these commands and a supplementary one to run all these commands at once and execute the last one with &lt;code&gt;npm run build-i18n&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
    "build-i18n:fr": "ng build --output-path=dist/fr --aot --prod --base-href /fr/ --i18n-file=src/i18n/messages.fr.xlf --i18n-format=xlf --i18n-locale=fr",
    "build-i18n:es": "ng build --output-path=dist/es --aot --prod --base-href /es/ --i18n-file=src/i18n/messages.es.xlf --i18n-format=xlf --i18n-locale=es",
    "build-i18n:en": "ng build --output-path=dist/en --aot --prod --base-href /en/ --i18n-file=src/i18n/messages.en.xlf --i18n-format=xlf --i18n-locale=en",
    "build-i18n": "npm run build-i18n:en &amp;amp;&amp;amp; npm run build-i18n:es &amp;amp;&amp;amp; npm run build-i18n:fr"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Apache2 configuration
&lt;/h2&gt;

&lt;p&gt;Here is a virtual host configuration which will serve your different bundles from the &lt;code&gt;/var/www&lt;/code&gt; directory: you will have to copy in this directory the three directories &lt;code&gt;en/&lt;/code&gt;, &lt;code&gt;es/&lt;/code&gt; and &lt;code&gt;fr/&lt;/code&gt; previously generated.&lt;/p&gt;

&lt;p&gt;With this configuration, the url &lt;a href="http://www.myapp.com" rel="noopener noreferrer"&gt;http://www.myapp.com&lt;/a&gt; is redirected to the subdirectory of the preferred language defined in your browser configuration (or &lt;code&gt;en&lt;/code&gt; if your preferred language is not found) and you still have access to the other languages by accessing the other subdirectories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
  ServerName www.myapp.com
  DocumentRoot /var/www
  &amp;lt;Directory "/var/www"&amp;gt;
    RewriteEngine on
    RewriteBase /
    RewriteRule ^../index\.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule (..) $1/index.html [L]
    RewriteCond %{HTTP:Accept-Language} ^fr [NC]
    RewriteRule ^$ /fr/ [R]
    RewriteCond %{HTTP:Accept-Language} ^es [NC]
    RewriteRule ^$ /es/ [R]
    RewriteCond %{HTTP:Accept-Language} !^es [NC]
    RewriteCond %{HTTP:Accept-Language} !^fr [NC]
    RewriteRule ^$ /en/ [R]
  &amp;lt;/Directory&amp;gt;
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  NGINX configuration
&lt;/h2&gt;

&lt;p&gt;Here is an NGINX configuration that will give you the same behaviour: an access to &lt;a href="http://www.myapp.com" rel="noopener noreferrer"&gt;http://www.myapp.com&lt;/a&gt; will redirect to the preferred language defined in the browser (or &lt;code&gt;en&lt;/code&gt; if your preferred language is not found) and the other languages are still accessible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen       80;
    server_name  localhost;

    location /en/ {
        alias   /usr/share/nginx/html/en/;
        try_files $uri$args $uri$args/ /en/index.html;
    }
    location /es/ {
        alias   /usr/share/nginx/html/es/;
        try_files $uri$args $uri$args/ /es/index.html;
    }
    location /fr/ {
        alias   /usr/share/nginx/html/fr/;
        try_files $uri$args $uri$args/ /fr/index.html;
    }

    set $first_language $http_accept_language;
    if ($http_accept_language ~* '^(.+?),') {
        set $first_language $1;
    }

    set $language_suffix 'en';
    if ($first_language ~* 'es') {
        set $language_suffix 'es';
    }
    if ($first_language ~* 'fr') {
        set $language_suffix 'fr';
    }

    location / {
        rewrite ^/$ http://localhost:4200/$language_suffix/index.html permanent;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: add links to the different languages
&lt;/h2&gt;

&lt;p&gt;It would be interesting to have some links in the app so the user can navigate to another languages by clicking these links. The links will point to &lt;code&gt;/en/&lt;/code&gt;, &lt;code&gt;/es/&lt;/code&gt; and &lt;code&gt;/fr/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One trick to know, the current language is available in the &lt;code&gt;LOCALE_ID&lt;/code&gt; token.&lt;/p&gt;

&lt;p&gt;Here is how you can get the &lt;code&gt;LOCALE_ID&lt;/code&gt; value and display the list of languages, differentiating the current language:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app.component.ts
import { Component, LOCALE_ID, Inject } from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  languages = [
    { code: 'en', label: 'English'},
    { code: 'es', label: 'Español'},
    { code: 'fr', label: 'Français'}
  ];
  constructor(@Inject(LOCALE_ID) protected localeId: string) {}
}
&amp;lt;!-- app.component.html --&amp;gt;
&amp;lt;h1 i18n&amp;gt;Hello World!&amp;lt;/h1&amp;gt;
&amp;lt;ng-template ngFor let-lang [ngForOf]="languages"&amp;gt;
  &amp;lt;span *ngIf="lang.code !== localeId"&amp;gt;
    &amp;lt;a href="/{{lang.code}}/"&amp;gt;{{lang.label}}&amp;lt;/a&amp;gt; &amp;lt;/span&amp;gt;
  &amp;lt;span *ngIf="lang.code === localeId"&amp;gt;{{lang.label}} &amp;lt;/span&amp;gt;
&amp;lt;/ng-template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bonus 2: Angular Translator application
&lt;/h3&gt;

&lt;p&gt;For the 2017 AngularAttack, I've created an application that can definitely help you translate your Angular applications. It is still in development, feedbacks are welcome: &lt;a href="http://angular-translator.elol.fr/" rel="noopener noreferrer"&gt;http://angular-translator.elol.fr/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Good translations!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>internationalization</category>
      <category>apache</category>
      <category>nginx</category>
    </item>
  </channel>
</rss>
