# Component Development Emerald project architecture is based in containers and containers orchestration. In this section we will cover the basics of component development. ## Table of contents - [Basics of component development](#basics-of-component-development) - [Basics of component publishing](#basics-of-component-publishing) - [Manual build and publish](#manual-build-and-publish) - [Dockerfile](#dockerfile) - [Build](#build) - [Login](#login) - [Publish](#publish) ## Basics of component development ## Basics of component publishing Independently of the component nature -open source or proprietary- it must have a repository in the Tecnalia GitLab. The repository must have a README.md and a LICENSE file. In the case of **open source** components, the README.md will describe the usage of the component, and the LICENSE file will contain the license of the component. By default, the license in Emerald project is Apache 2.0. In the case of **proprietary** components, the README.md will describe the objective of the component, and how to access the component. The LICENSE file will contain the license of the component. **Note: for the public release of the proprietary components, only the README and the LICENSE files will be copied to the public repository.** ## Component placement in the Tecnalia GitLab The components must be placed in the Tecnalia GitLab. The components must be placed in the `emerald/private/components` group. Inside we have prepared individual groups for each component, e.g., `emerald/private/components/amoe`, `emerald/private/components/rcm`, etc. **Note: if your component is not in the list, please contact the CaaS Framework administrator to create the group for your component.** Inside the component group, you can create a repo for each of the images that your component require. For example, the `rcm` component has the following repositories: `frontend`, `backend` and `converter`. Adittionally, if you require more repositories for your component, you can create them. For example, - The `amoe` component has an internal group `emerald/private/components/amoe/python-clients` that contains the code generated with the client libraries. - The `rcm` has a repository `docker-compose` that contains a composition that we use to build rcm; and it also has an `initial-data` repository that contain the initial configuration data that we use in the docker-compose project. ## Manual build and publish The simplest way to build and publish a component is manually. It is useful for testing purposes. To build and publish a component manually, you will need a Dockerfile. ### Dockerfile The Dockerfile is a text file that contains the instructions to build a docker image. The Dockerfile must be placed in the root of the component repository. The following is an example of a Dockerfile: ```Dockerfile FROM python:3.8-slim WORKDIR /app COPY requirements.txt requirements.txt RUN pip install -r requirements.txt COPY src src CMD ["python", "src/main.py"] ``` The above file is a simple example of a python application. It installs the requirements, copies the source code and runs the main.py file. ### Build To build the docker image, you can use the following command: ```bash docker build -t my-component:latest . ``` that will build the docker image with the tag `my-component:latest`. ### Publish To publish the docker image in some binary repository, you need first to login into the docker image registry. You can use the following command: ```bash docker login -u emerald -p <secure-token> emerald-docker-dev-local.artifact.tecnalia.com ``` To publish the docker image in Tecnalia's Artifactory, you can use the following command: ```bash docker tag my-component:latest emerald-docker-dev-local.artifact.tecnalia.com/my-component/my-component:latest docker push emerald-docker-dev-local.artifact.tecnalia.com/my-component/my-component:latest ``` ## Dockerfile Recommendations When developing the dockerfile you should have in mind that depending of how you write the dockerfile, the resulting docker image will be more (or less) efficient, stable or large. Here are some recommendations: - **SIZE**. Check the size of the resulting image. The size of the image is important because it affects the time it takes to download the image, the time it takes to start the container, and the amount of disk space used. You can use the `docker image ls` command to check the size of the image. If it is too big, you can try to reduce the size by removing unnecessary files, using a smaller base image, or using multi-stage builds. - **SET-UP FILES**. Avoid including large setup files inside the image. If you need to include them, you can use a volume to mount the files at runtime. This will reduce the size of the image. - **BASE IMAGES**. Use versioned base images. For example, if you are developing a python application, you can use the `python:3.8` image instead of the `python:latest` image. Using the `latest` tag can lead to unexpected behavior when the base image is updated. - **IMAGE SIZE**. Use the smallest base image that you can. For example, if you are developing a python application, you can use the `python:3.8-slim` image instead of the `python:3.8` image. - **DEPENDENCIES**. Install dependecies at the beginning of the Dockerfile. This will allow you to take advantage of the docker cache, so that if you change the source code, the dependencies will not be installed again, saving lots of time. For example: ```Dockerfile FROM python:3.8-slim WORKDIR /app COPY requirements.txt requirements.txt RUN pip install -r requirements.txt COPY src src CMD ["python", "src/main.py"] ``` - **MULTI-STAGE**. If your application requires a build stage with packages that are not needed in the final image, you can use multi-stage builds. For example: ```Dockerfile FROM maven:3.6.3-jdk-11 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src src RUN mvn package FROM openjdk:11-jre-slim WORKDIR /app COPY --from=build /app/target/my-app.jar . CMD ["java", "-jar", "my-app.jar"] ``` - **COPY vs ADD**. Use COPY instead of ADD. The ADD instruction is more powerful than the COPY instruction. However, the ADD instruction can be confusing and lead to unexpected behavior. For example, the ADD instruction can download files from the internet. This can be a security risk. If you need to ensure permissions after COPY use `--chown` option. For example, if you copy a file and you need to ensure that the file has the correct permissions, you can use the `--chown` option. i.e.: ```Dockerfile FROM python:3.8-slim WORKDIR /app COPY --chown=1000:1000 src src CMD ["python", "src/main.py"] ``` - **COMBINE INSTRUCTIONS**. Combining instructions, you can reduce the number of layers in the docker image. Do not forget to clean up the cache in the same instruction. Use *Heredoc* syntax to combine instructions. For example: ```Dockerfile FROM python:3.8-slim WORKDIR /app RUN <<EOF set -e apt-get update export DEBIAN_FRONTEND=noninteractive apt-get install -y \ build-essential \ libpq-dev apt-get clean rm -rf /var/lib/apt/lists/* EOF COPY requirements.txt requirements.txt RUN pip install -r requirements.txt COPY src src CMD ["python", "src/main.py"] ``` - **USE EXPOSE**. The EXPOSE instruction informs not only the user, but also other utility containers (i.e. traefik) that the container listens on the specified network ports at runtime. For example: ```Dockerfile FROM python:3.8-slim WORKDIR /app COPY requirements.txt requirements.txt RUN pip install -r requirements.txt COPY src src EXPOSE 8080 CMD ["python", "src/main.py"] ``` - **(Optional) USE HEALTHCHECK**. The HEALTHCHECK instruction tells Docker how to test a container to check that it is working. This can be used to detect when a container is unhealthy. For example: ```Dockerfile FROM python:3.8-slim WORKDIR /app COPY requirements.txt requirements.txt RUN pip install -r requirements.txt COPY src src EXPOSE 8080 HEALTHCHECK --interval=5m --timeout=3s \ CMD wget -qO- http://localhost:8080/health || exit 1 CMD ["python", "src/main.py"] ``` - **(Optional) DEFINE VOLUMES**. The VOLUME instruction creates a mount point with the specified name and marks it as holding externally mounted volumes from native host or other containers. For example: ```Dockerfile FROM python:3.8-slim WORKDIR /app COPY requirements.txt requirements.txt RUN pip install -r requirements.txt COPY src src EXPOSE 8080 VOLUME /app/data CMD ["python", "src/main.py"] ```