Docker and casual work/dev on virtual machine and IDE - spring-boot

I have a general question about good practices and lets say way of work between docker and IDE.
Right now i am learning docker and docker compose, and i must admit that i like the idea of containers! Ive deployed my whole spring boot microservices architecture on containers, and everything is working really well!
The thing is, that in every place of properties when i am declaring localhost address, i was forced to change localhost to custom container names, for example localhost:8888 --> naming-server:8888. It is okay for running in containers, but obviously when i am trying to run this on IDE, it will fail. I like working/optimizing/debugging microservices in IDE, but i dont want rebuilding image and returning whole docker-compose every time i made a tiny small change.
What does it look like in real dev?
Regards!

In my day job there are at least four environments my code can run in: my desktop development environment, a developer-oriented container environment, and pre-production and production container environments. All four of these environments can have different values for things like host names. That means they must be configurable in some way.
If you've hard-coded localhost as a hostname in your application source code, it will not run in any environment other than your development system, and it needs to be changed to a configuration option.
From a pure-Docker point of view, making these configurable via environment variables is easiest (and Spring can set property values from environment variables). Spring also has the notion of a profile, which in principle matches the concept of having different settings for different environments, but injecting a whole profile configuration can be a little more complex at deployment time.
The other practice I've found helpful is to have the environment variable settings default to reasonable things for developers. The pre-production and production deployments are all heavily scripted and so there's a reasonably strong guarantee that they will have all of the correct environment variables set. If $PGHOST defaults to localhost that's right for a non-Docker developer, and all of the container-based setups can set an appropriate value for their environment at deploy time.
Even though our actual deployment system is based on containers (via Kubernetes) I do my day-to-day development in a mostly non-Docker environment. I can run an individual microservice by launching it from a shell prompt, possibly with setting some environment variables, and services have unit tests that can run just on the checked-out source tree, without needing any Docker at all. A second step is to build an image and deploy it into the development environment, and our CI system runs integration tests against the images it builds.

Related

Best way to create image for different environment

What is the best way to maintain Images for different environments and why?
Option : 1
Create diff images for the specific environment dev, stg, prod. we have to tell Jenkin job for which environment we are building the image and spring boot will load the specific configuration files.
advantages :
Environment specif images.
disadvantages :
Every environment will have diff images so we have to build it everytime.
Option : 2
Build 1 image, externalize the config file. While building the image create a shared/mount path place an appropriate config file. While initialization load the config file.
advantages:
One image can be used by all the environment.
disadvantages :
Custom Configuration handling.
Need coordination between 2 teams.
Let me know if there are other options and whats the advantages and disadvantages above approach or any other approach are present.
Build once and deploy anywhere is considered as the fundamental principles of the continuous delivery (Google it for its advantages). So I would build the same image for all environments . And when running the image , it needs to have some ways to allow configuring these configurations based on the environment.
In term of docker , it allows to configure the environment variables when running a container (e.g see this in case of docker-compose)
In term of spring-boot, the OS environment variables will override the application properties in the app.
When designing your images, split the filesystem and environment into 3 pieces.
Binaries, runtime, libraries, code needed to run the application. This belongs in your image.
Configurations and secrets that will differ for different users of the image. These belong in configuration files and environment variables that are injected at runtime (either as a bind mount, docker-compose.yml env vars, k8s config map, etc).
Data. This should be mounted as a volume, or in an external database accessed with a configuration/secret.
This keeps with the 12 factor design and enables portability, easier testing, and less risk with deploying something into production that is different from what was tested in CI.
You can build docker image for each environments separatly.
for example: read variable of dev environment from .env.dev file.
create images that contain specific configurations for every environment

Testing app that depends on environment variables locally

Google's App Engine provides a list of predefined environment variables and additional environment variables may be defined in app.yaml. Meanwhile, the instructions for Testing and Deploying your Application just say to use go run to test the app locally. If I test my app locally inside a cloud-sdk Docker container, is there a gcloud command (or another tool) that would create the same environment variables in my local container as in App Engine? Right now I am just setting the environment variables locally with a bash script, but that means that I need to maintain the variables in multiple locations.
The variables are all runtime metadata. Only the runtime can provide values for these variables and then the data is specific to the deployment.
If your app needs this metadata, you will know which variables it uses and how it uses them and, when you specify the value, you will need to provide the variable name anyway, e g. GAE_SERVICE="freddie".
For these reasons, it's likely not useful for local testing to spoof these values for you. When you go run your app, there's nothing intrinsic about it that makes it an App Engine app. It only becomes one, after you deploy it, because it's running on the App Engine service.
If you're running your code in a container, you can provide environment variables to the container runtime. Doing so is likely preferable to scripting these:
GAE_SERVICE="freddie"
docker run .... \
--env=GAE_SERVICE=${GAE_SERVICE} \
...
Although not really practical with App Engine, there's an argument for having your code not bind directly to any runtime (e.g. App Engine) metadata. If it does, you can't run it easily elsewhere.
In other platforms, metadata would be abstracted further and some sort of sidecar would convert the metadata into a form that's presented consistently regardless of where you deploy it; your code doesn't change but some adapter configures it correctly for each runtime.

What are the best gitlab-ci.yml CI/CD practices and runners configs?

This is a bit theoretical, but I'll try to explain my setup as much as I can:
1 server (instance) with a self-hosted gitlab
1 server (instance) for development
1 server (instance) for production
Let's say in my gitlab I have a ReactJs project and I configured my gitlab-ci.yml as following:
job deploy_dev Upon pushing to dev branch, the updates will be copied with rsync to /var/www/html/${CI_PROJECT_NAME} (As a deployment to dev server)
The runner that picks up the job deploy_dev is a shared runner installed on that same dev server that I deploy to and it picks up jobs with the tag reactjs
The question is:
If I want to deploy to production what is the best practice should I follow?
I managed to come up with a couple of options that I thought of but I don't know which one is the best practice (if any). Here is what I came up with:
Modify gitlab-ci.yml adding a job deploy_prod with the same tag reactjs but the script should rsync with the production server's /var/www/html/${CI_PROJECT_NAME} using SSH?
Set up another runner on production server and let it pick up the jobs with tags reactjs-prod and modify gitlab-ci.yml to have deploy_prod with the tag reactjs-prod?
You have a better way other than the 2 mentioned above?
Last question (related):
Where is the best place to install my runners? Is what I'm doing (Having my runners on my dev server) actually ok?
Please if you can explain to me the best way (that you would choose) with the reasons as in pros or cons I would be very grateful.
The best practice is to separate your CI/CD infrastructure from the infrastructure where you host your apps.
This is done to minimize the number of variables which can lead to problems with either your applications or your runners.
Consider the following scenarios when you have a runner on the same machine where you host your application: (The below scenarios can happen even if the runner and app are running in separate Docker containers. The underlying machine is still a single point of failure.)
The runner executes a CPU/RAM heavy job and takes up most of the resources on the machine. Your application starts experiencing performance problems.
The Gitlab runner crashes and puts the host machine in an inoperable state. (docker panic or whatever).
Your production app stops functioning.
Your app brakes the host machine (Doesn't matter how. It can happen), your CI/CD stops working and you can not deploy a fix to production.
Consider having a separate runner machine (or machines. Gitlab runner can scale horizontally), that is used to run your deployment jobs to both dev and production servers.
I agree with #cecunami's answer.
As an example, in our Org we have a dedicated VM only for the runner, which is explicitly monitored by our teams.
Since first creating the machine, the CPU, RAM and storage demand has grown massively, thus why the infrastructure is to be separated.

How can I have separate APIs for staging and production environments on Heroku?

I was just checking on how pipelines work in Heroku. I want the staging and production apps to be the same except that they should access different API endpoints.
How could I achieve that?
Heroku encourages getting configuration from the environment:
A single app always runs in multiple environments, including at least on your development machine and in production on Heroku. An open-source app might be deployed to hundreds of different environments.
Although these environments might all run the same code, they usually have environment-specific configurations. For example, an app’s staging and production environments might use different Amazon S3 buckets, meaning they also need different credentials for those buckets.
An app’s environment-specific configuration should be stored in environment variables (not in the app’s source code). This lets you modify each environment’s configuration in isolation, and prevents secure credentials from being stored in version control. Learn more about storing config in the environment.
On a traditional host or when working locally, you often set environment variables in your .bashrc file. On Heroku, you use config vars.
In this instance you might use an environment variable called API_BASE that gets set to the base URL of your staging API on your staging instance and to the base URL of your production API in production.
Exactly how you read those values depends on the technology you're using, but if you look for "environment variables" in your language's documentation you should be able to get started.

Docker based development environment for multiple projects

I am wondering about the best architecture for a Docker based development environment with a LAMP stack.
Requirements
Working on multiple projects in parallel
Most projects are using the same LAMP stack (for simplicity let's assume that all projects are sharing the same stack and configuration)
The host is running Windows + VBox + Docker Toolbox (i.e. Boot2Docker)
Current architecture
One shared development environment running multiple containers (web, db, persistent data..) with vhosts configuration per site
Using scripts / Jenkins container to setup new project (new DB, vhost configuration..)
Running custom Samba container to share the data with the Windows machine (IDE is running on Windows)
As always there are pros. and cons., while this is quite easy for maintenance, we are unable to really deploy a specific project with a dedicated docker-compose.yml file, and we are also unable to get all the benefits of "micro services" like replacing PHP / MySQL version for a specific site.
The question is how can we use a per project docker-compose.yml file, but still have multiple projects running simultaneously (since all projects are using port 80).
Will it be better (and is it even possible?) to use random ports per project and run a proxy layer on top of those web containers?
Any other options or common design patterns for this use case?
Thanks.
The short answer is yes. Docker by default assigns random ports if no port it is specified. For mapping I would use: https://github.com/jwilder/nginx-proxy
You can have something like project1.yml project2.yml .... and to start the containers would be something like:
docker-comppse -f project1.yml up
However, I'm not sure why would you try to do that. You could use something like Rancher and split my development host into multiple small development environments.

Resources