# Question
Title: Is AWS Fargate suitable for web applications?
I'm making a web-app, I've tried both ECS and EKS at AWS. It's been very comfortable to use Fargate instances instead of EC2. But I'm not sure I understand how to use them since when I count at it, it's so much more expensive. Prices

In the prices it says ~$0.05/h/vCPU which would mean 37.5$/m/vCPU. Which is very expensive compared to EC2. But from what I've understood Fargate is more suitable for container that have a limited lifetime? Does this mean that they are not useable at all(or atleast price worthy) for a webapp that's running 24/7? My web app can really shutdown an start up by requests, it would be to slow. Is there something I'm missing?

# Answer
> 6 votes
Fargate is absolutely suitable for web apps. We have many customers that use it that way (and many others that use it in a more "batchy" way). Fargate is a managed compute engine and AWS takes a more responsibilities in terms of scaling, patching, security, and so forth so comparing raw Fargate costs to raw EC2 costs isn't how I would compare them. Having that said, raw EC2 capacity (like for like) is usually just 20% cheaper than raw Fargate capacity. If you factor in the cost of operations (less with Fargate) and the fact that you hardly drive EC2 clusters at full utilization, the comparison tends to skew towards Fargate most of the times. These are a couple of blogs that talk about this (blog one and blog two).

# Answer
> 4 votes
> But from what I've understood Fargate is more suitable for container that have a limited lifetime?

That's not strictly correct, Fargate capabilities include tasks which are suitable for short-term limited lifetime tasks as well as services which are designed to run all the time.

For sure, Fargate is more expensive as compared to EC2 - but you don't need to maintain the instance, patch the OS, have your container runtime and manage them, handle scaling of the underlying nodes - Fargate does this all for you. All you need to provide is a Docker image, how much vCPUs & Memory you need and to how many containers it should scale to, and AWS will take care of the rest. You can optionally provide a CloudWatch log group and have the container logs streamed to CloudWatch logs, or also stream them to custom log ingesters as well.

# Answer
> 2 votes
Nope, Fargate can be used for long running containerized workloads. Web apps, proxies, or any other containers. Maybe you are thinking of Fargate Spot? Fargate Spot is definitely not suitable for long running containers, and should only be used for running workloads that can be safely interrupted or stopped unexpectedly. This is because AWS is able to retake those instances from you at any point in time they choose.

---
Tags: amazon-web-services, amazon-ecs, eks
---


# Question
Title: Jenkins and Kubernetes Design Involving GitHub
I am new to DevOps and am currently designing a CI/CD pipeline that involves integrating GitHub webhooks with Jenkins in a Kubernetes environment, and I would like to see whether this is feasible or not.

Currently, I have a GitHub repository setup with some code. In Kubernetes, we have a volume setup where we plan to clone said repository into this volume. This repository will need to be up-to-date at all times, but we shouldn't manually update this repository every time a change is made. With Jenkins, we want to setup webhooks in GitHub so any time changes made in the code repository will be updated in the volume.

I am still new to the idea of webhooks and Jenkins will interact in a Kubernetes environment, specifically a volume, so any thoughts and comments would be appreciated.

Thanks in advance!

# Answer
> 1 votes
I would use the webhooks GitHub provides if you don't need something specific from Jenkins (Jenkins is in many cases a consumer of GitHub events/webhooks).

https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#push

Using this webhook you could inspect each payload for specific parameters (ref, repo, etc...) and do something (like pull) if those parameters are met. In either case, you will need a service that can process the webhook/payload and perform the work/automation.

---
Tags: jenkins, kubernetes, cicd, github
---


# Question
Title: Salt - Need state to check pillar and perform a if/else check
This is salt 101 I think, but I'm really a salt N00b and need assistance.

I need to disable specific services on all of our servers by default, but I need to create a pillar that allows me to override this, and allow the specific servers mentioned in pillar to have those services running. For the sake of discussion, let's assume the service is httpd.

Currently I have a VERY simple state named "disable\_httpd.sls" that says this...

```
{% from "security/map.jinja" import security with context %}
stop and disable httpd-server:
  service.dead:
    - name: httpd
    - enable: False

```

Now we do have a few web servers where we need to allow it, and obviously my state, which is called as part of our security items, will break those servers.

I need to figure out how to write a pillar that will exempt specific servers.

Thoughts?

---

Update: Here's what I have tried so far, but it's not working.

Note that this is being tried in a lab, and the system I'm playing with is named "saltminion1.local".

1. Changed the state (disable\_httpd.sls) to say this...

```
{% from "security/map.jinja" import security with context %}

{% set allow_httpd = salt['pillar.get']('security:options:allow_httpd', "") %}
{% if "True" in allow_httpd and allow_httpd is string %}
Make sure httpd service is running:
  service.enabled:
    - name: httpd

{% else %}

stop and disable httpd:
  service.dead:
    - name: httpd
    - enable: False

{% endif %}

```

Then in pillar I created a file named "saltminion1.sls" in the "security" directory.

```
security:
  options:
    allow_httpd: 'True'

```

I modified pillar/top.sls to add this section...

```
prod:
  'saltminion1.local':
    - match: nodegroup
    - security.saltminion1

```

The thought is that it should pick up the "True" in the saltminion1.sls pillar file, and allow httpd. What it's actually doing is the "else" section of the state, and killing the service.

# Answer
> 1 votes
It looks like you are on the right approach here, the issue seems to be one of debugging. I have found this jinja2 live templating engine to be helpful with debugging in the past.

In python (on which jinja is based) "True" is a keyword which is probably getting interpreted as a boolean data type. This would be in contrast to the string, integer, or floating point (decimal) data types which are a C/C++ concept that python inherits since it's interpreter is written in C. Python tries to be agnostic to data type and variables are not strongly typed. This appears to be a situation in which the lack of strong typing may be getting you into some hot water.

In your boolean logic in the "if" block says to only render the section if `allow_httpd` is a string - but it isn't! It's a boolean! This is likely the error. Instead, you may wish to try simply:

```
{% if allow_httpd %}
Make sure httpd service is running:
  service.enabled:
    - name: httpd

{% else %}

stop and disable httpd:
  service.dead:
    - name: httpd
    - enable: False

{% endif %}

```

I'd also recommend you remove your quotes in the pillar data if you use this approach.

---
Tags: salt, pillar
---


# Question
Title: Jinja syntax error: missing endif, in spite of providing it
I am using Salt 2019.2.0 version and below is my state file where I am using if condition to check the values and run states if it's a match. Even if I have provided endif, it says Jinja looking for missing endif tag. Any help please?

**CODE**:

```
{% if salt['pillar.get']('spade:Corename') == 'FI' and salt['pillar.get']('spade:NodeType') == 'master' %}
dataimport-script:
  file.managed:
    - name: /opt/bin/DataImport.py
    - source: salt://files/DataImport.py

dataimport-crontab:
  file.managed:
    - name: /etc/cron.d/solr-dataimport
    - contents: |
                # # set up data import for FI every 2 minutes
                */2 * * * * root /usr/bin/python /opt/bin/DataImport.py
{% elif salt['pillar.get']('spade:NodeType') in ['slave','ds'] and salt['pillar.get']('spade:Corename') == 'FI' %}
update-fi-solrconfig:
  file.replace:
    - name: {{ salt['pillar.get']('solr:home_dir') }}/data/{{ salt['pillar.get']('spade:Corename') }}/conf/solrconfig.xml
    - pattern: '"autoDeletePeriodSeconds">30'
    - repl: '"autoDeletePeriodSeconds">-1'
{% endif %}

```

**ERROR**:

```
local:

Data failed to compile:
----------
Rendering SLS 'base:solr.install-solr' failed: Jinja syntax error: Unexpected end of template. Jinja was looking for the following tags: 'endif'. The innermost block that needs to be closed is 'if'.; line 205
---

[...]

update-fi-solrconfig:
file.replace:
- name: {{ salt['pillar.get']('solr:home_dir') }}/data/{{ salt['pillar.get']('spade:Corename') }}/conf/solrconfig.xml
- pattern: '"autoDeletePeriodSeconds">30'
- repl: '"autoDeletePeriodSeconds">-1'
{% endif %} <======================

```

# Answer
> 2 votes
There is no error in what you have posted here. The error lies elsewhere in your code which you did not include in the OP. Using the Jinja live parser, when pasting in:

```
{% if spade.corename == 'FI' and spade.nodetype == 'master' %}
dataimport-script:
  file.managed:
    - name: /opt/bin/DataImport.py
    - source: salt://files/DataImport.py

dataimport-crontab:
  file.managed:
    - name: /etc/cron.d/solr-dataimport
    - contents: |
                # # set up data import for FI every 2 minutes
                */2 * * * * root /usr/bin/python /opt/bin/DataImport.py
{% elif spade.nodetype in ['slave','ds'] and spade.corename == 'FI' %}
update-fi-solrconfig:
  file.replace:
    - name: {{ salt['pillar.get']('solr:home_dir') }}/data/{{
salt['pillar.get']('spade:Corename') }}/conf/solrconfig.xml
    - pattern: '"autoDeletePeriodSeconds">30'
    - repl: '"autoDeletePeriodSeconds">-1'
{% endif %}

```

With a pillar of:

```
spade:
  corename: FI
  nodetype: master

```

Everything renders just fine. This tells me there is nothing wrong with your snippet. The fact that your error indicates you have at least 205 lines of code, but the example has far less tells me the issue was with another "if" block further up in your code.

---
Tags: salt, jinja
---


title: Best Kubernetes storage option

text:
# Question
Title: Best Kubernetes storage option
I know there are several options, I’ve heard everything from SSHFS, NFS, object storage all the way to storage OS, my issue is I need something fast and reliable to serve a media website built on Wordpress or similar cms. However I may want to do something with django also.

I have a home server I’ve been using over sshfs for some things but for a media site I don’t think that would be fast enough.

Any suggestions on the best method to store things? I also need to be able to access these items on an as needed basis through SFTP or an interface. I’ve heard more than once that storageOS is the best option but after reviewing their site I don’t understand how it is persistent storage if it’s a container that is stored on an inaccessible node.

How can you back that up, manage space, etc?

I’m sure that a lot of you were looking at this question laughing because it seems so silly. Then there are probably some people who are just learning and saying OK yeah this is a legitimate question and then there are probably some people newer than myself you don’t even realize they need storage yet.

# Answer
> 1 votes
Your question is not silly at all, indeed managing WordPress at scale requires broad knowledges due to how WordPress is implemented.

It's good to know that WordPress consists of 1) core PHP code, 2) plugins/themes and 3) uploaded media assets. To serve the core and plugins, you'll need to locate the code either in local disk or NFS. Local disk is obviously faster since it doesn't interact with network, but NFS is a good choice if you want to have multiple servers to share the same code.

The uploaded media assets are a bit different. The most common "assets" are image files, and you can completely offload the server traffic to object storage such as AWS S3 or GCP Cloud Storage. Unlike plugins and themes, the media assets are **static** meaning it will never run server-side logics like PHP.

Moreover, WordPress is **read-heavy** application that most of the MySQL query is to render blog posts to your public audiences, means any-sized server may blow up when unexpected number of visitors rush to your website. This is where CDN shines, to cache rendered HTMLs in the edge servers at the first visit, then serve the cached files without reaching to origins. AWS CloudFront and GCP Cloud CDN are popular CDNs to turn cheap WordPress server to globally-scalable media site.

So, I recommend to have the following architecture:

```
Visitor --> CDN --> Web/PHP Server --> MySQL

```

When configured correctly, you can serve millions of traffics backed with $10 web server.

---
Tags: docker, kubernetes, storage
---


# Question
Title: Istio Keeps On Showing TcpProxyValidationError Errors
I initially created an EnvoyFilter to apply `idle_timeout` of `5s` to outbound requests originating from workloads with label `app: mecha-dev`.

```
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: tcp-idle-timeout
spec:
  workloadSelector:
    labels:
      app: mecha-dev
  filters:
    - listenerMatch:
        listenerType: SIDECAR_OUTBOUND
        listenerProtocol: TCP
      filterName: envoy.tcp_proxy
      filterType: NETWORK
      filterConfig:
        idle_timeout: 5s

```

However, the filter did not work for me and I found out it was because my cluster is using `istio v1.2.5` which is a very old version and does not support `EnvoyFilters` properly and the envoy API for the same have been deprecated.

Therefore, I deleted the `EnvoyFilter` using `kubectl`.

But, even after deletion of the filter the `istio-proxy` sidecar containers keep on showing me the following error again and again:

```
[2021-05-11 11:59:14.109][22][warning][config] [external/envoy/source/common/config/grpc_mux_subscription_impl.cc:73] gRPC config for type.googleapis.com/envoy.api.v2.Listener rejected: Error adding/updating listener(s) 10.155.0.113_443: Proto constraint validation failed (TcpProxyValidationError.StatPrefix: ["value length must be at least " '\x01' " bytes"]): idle_timeout {
  seconds: 5
}

```

I deleted the pods and re-created new ones but this error keeps on showing up. Is there anything else I need to clean up or delete to get rid of this error?

# Answer
Restarting Istio controlplane should fix the issue on hand (pods in `istio-system`, most likely pilot).

More importantly do not run Istio 1.2 its very outdated version with many bugs and security issues.
> 1 votes

---
Tags: kubernetes, istio
---


# Question
Title: How do you stop malicious requests in Azure APIM from repeatedly hitting your rate or quota limits?
You have an open facing Azure API Gateway that can be consumed by anyone and you want to protect yourself from the API being spammed by the same IP or the same individual behind multiple IP's.

There are policies one can configure within API Management to:

Setting limits by subscription (specific consumers) or key (specific apps) wouldn't work because your API is open to the public, and the last option of restricting caller IP's one-by-one doesn't prevent the issue from happening in the first place.

How do you protect against the same user (IP) repeatedly crushing your rate or quota limits? Additionally, how would you protect against that same user doing this across multiple IP's?

My guess is that this is likely covered in Azure's DDoS Protection Basic but I felt that I couldn't get a concrete answer out of those docs.

# Answer
You can rate limit or throttle based on the IP: https://docs.microsoft.com/en-us/azure/api-management/api-management-sample-flexible-throttling#ip-address-throttling

Also, Azure DDoS Protection Basic doesn't cover much. It won't know what is normal or not for your application without paying the ~$3,000 per month for the Standard plan.
> 1 votes

---
Tags: azure-devops, azure
---


# Question
Title: How to get code onto aws ec2 instance using terraform and gitlabs
I've just started learning Terraform (utilizing the AWS provider) and I have managed to replicate our infrastructure which includes quite a few amazon web services.

I can successfully deploy it and get a basic Apache website up and running on an EC2 instance with the following bash script (run from Terraform):

```
#!/bin/bash
sudo apt update -y
sudo apt install apache2 -y
sudo systemctl start apache2
sudo bash -c 'echo frontend-website > /var/www/html/index.html'

```

Very basic, but I'm having issues in finding some documentation on the best practices for getting our project code onto the AWS EC2 instance once provisioned, and how everything should be setup for Gitlab CI/CD to keep it all automated.

Especially for a bigger project (e.g., separate frontend and backend instances) where you may have a dev, test, and prod environment for the frontend and backend.

# Answer
Setup a Jenkins server. Please do code checkout using Jenkins. Jenkins supports parameterized build. you can pass selected parameter from Jenkins UI to linux bash.

Then use linux's cli command to fire terraform.Terraform supports several workspace. You can use same code for dev, test and prod.

`terraform workspace new dev` Use this command to create new workspace. After that , we can use this workspace name to your code. Below is the content of `local.tf`

```
locals {
  env = terraform.workspace
}

```

Like below use the same code for multiple env.

```
resource "aws_autoscaling_group" "asg" {
  name = "${local.env}-${local.project}-demo-asg"
}

```

Moreover to save your state file securely , you can use s3 as backend.

```
terraform {
  backend "s3" {
    bucket = "tfstate-demo"
    key = "keyfiles"
    region = "ap-southeast-1"
    dynamodb_table = "tfstate-demo"
  }
}

```

To separate backend and frontend service, use best practice of AWS. Create a public ALB for frontend and then move traffic to backend services via private ALB.

You can use Ansible as a configuration manager to build your server then create a AMI of that using packer. Later on, use the same AMI in ASG.
> 1 votes

# Answer
The way we do this is to use EC2 User data to run a cloud-init script after the EC2 instance has been launched.

With Terraform this user data can be templated and parameterized for different environments using Terraform's templatefile and passing in variables via parameter files.

I would suggest you also read up on AWS Auto scaling groups and launch templates as well.
> 0 votes

---
Tags: amazon-web-services, terraform, gitlab, amazon-ec2
---


# Question
Title: How can I add another module to my existing Terraform deployment plan?
I have deployed a project within Terraform. I need to add a 4th application module to my existing plan. How do I update my plan to include this 4th module and deploy the missing module without starting over? I thought terraform import might be an option, but I'm not understanding how it would be done.

# Answer
Terraform is idempotent so will only change what needs to be changed.

So after applying your existing plan, if you add more modules or resources and run another plan and apply, it will add the new resources not the whole infrastructure.

Terraform import is used when you want to start managing a resource created outside your Terraform code for example created in the AWS console, AWS CLI).

It basically associates and existing resource with your new Terraform code, so that Terraform doesn't try to recreate it when you run the plan/ apply.
> 2 votes

---
Tags: terraform, terraform-module
---


# Question
Title: Are there automated quality checking tools for Ops code
In the dev world, we have several tools available to us for automated quality checks. We have unit tests that run on each build, Sonar to check for code quality, metrics being sent to Grafana and Okta with automated alarms, etc.

I'm not very familiar with the ops world; are there similar automated tools available for our ops code?

For Terraform scripts, Dockerfiles, Helm/K8 scripts, Cloudbees configurations and other ops code, the concerns I would like to see automation for are:

* Syntax checking
* Code smells / anti patterns
* Routing / Connection checking for accuracy
* Security best practice checking (don't expose these ports or include these credentials,or grant these permissions kind-of-a-thing)
* Appropriate provisioning (too many or too few resources allocated for the size of the task)
* High cost warnings
* Unit test-ish tools to ensure we aren't breaking the build in more subtle ways than "did it deploy successfully or not"

There's likely a lot more that I don't even know to look for given my lack of experience. Are there support tools that will analyze our ops code that could provide automated support in this fashion?

# Answer
It's difficult to provide a definitive answer given the scope of the question, but the goal of testing infrastructure is both laudable and seldom done at all.

To my knowledge, you are looking for something akin to Open Policy Agent which provides a general policy language in which to express arbitrary policy as code. I would suggest investigating that at length.

At the risk of not providing a complete answer, I will give my best shot for some concrete examples as well.

## Compliance as Code

In general, you need something which is often referred to as a "compliance framework", and in the DevOps world more often referred to as "Policy as Code". This allows you to define policies as code, which really means "machine-readable and executable". You specify what the policy is in the DSL of the framework, it is executed at some stage of your pipeline, and fails the change if it violates the policy.

To illustrate the point, consider someone submitting a pull request to a terraform module that inadvertently opens a port on a security group to the world which the policy says should be closed. This may be difficult to determine when you're writing the code, but the compliance test will catch it.

There are a few ways to do this, but I will mention two. A notable exception is Pulumi's Crossguard, but I don't have enough experience to speak to that.

## Hashicorp Sentinel

In the Hashicorp world, there is the Sentinel Framework. This is an Enterprise product, so may not be relevant to your situation, but it is one of the best implementations I've seen. Importantly, it has a built-in testing framework which aids in writing good policy code, which is also very expressive. I have used this as inspiration for how to write policy in Chef Inspec, which is the other example I will cite.

## Chef Inspec

Chef Inspec is a framework for writing policy as code using rspec-style assertions. It uses a DSL based on ruby and supports major clouds (AWS, GCP, Azure) as well as OS-level resources. It is open source, so you can use it anywhere. You use it to make assertions in individual *resources* which are defined in the inspec DSL, *e.g.* "user on an OS" or "AWS EC2 instance". Writing the tests consists of assertions against the actual state of those resources, which makes it quite like a unit test. However, Inspec does not have a built-in testing framework for itself like Sentinel does - although you could use the ruby ecosystem for doing this.

---

Some notes:

1. Both of these tools allow you to express your policy as code, and to start extending the test coverage of that policy over your existing infrastructure.
2. Sentinel will catch violations before they are applied, but Inspec needs to be executed against an already-provisioned state.
3. Both will do what you required, but depend on your tech stack as to whether they will be appropriate for you
4. There is no magic bullet
    1. for both of them you will need to write your policy as code. Usually this means taking some form of human-readable documentation and writing it in the DSL of the framework.
    2. On the other hand, this provides an excellent opportunity to collaborate with the security team and do what folks are calling DevSecOps these days.
> 3 votes

# Answer
The tools that we have include in our Terraform pipelines to check code quality and best practises are;

1. Tflint
2. TFSec
3. Checkov

This any issues get highlighted early at the MR stage.
> 0 votes

---
Tags: terraform, automation, quality-cotrol
---


title: Is there a way to lint Terraform?

text:
# Question
Title: Is there a way to lint Terraform?
I'm hoping there is something to pick up when I have misspelled variables and comments to save me some time when I'm checking my code and reduce the potential for any errors.

I normally use IntelliJ IDE for Terraform on a MAC but also have VS code set up that I can use.

Is there a plugin for either I could use or even a different tool? I imaging variable might be difficult to lint due as I use underscores to separate words in my variable names so the tool would need to know to treat underscores as white space for linting purposes.

# Answer
`terraform fmt`

> The `terraform fmt` command is used to rewrite Terraform configuration files to a canonical format and style.
> 11 votes

# Answer
The tools that we have include in our Terraform pipelines to check code quality and best practises are;

1. Tflint
2. TFSec
3. Checkov

This way any issues get highlighted early at the MR stage.
> 5 votes

# Answer
VSCode has a Terraform Linter "l2fprod.terraform-fork" Have NOT tried it. Did a simple search terra + lint. HTH DrewBai
> 1 votes

# Answer
You can use tflint project. It has lot of plugins for cloud providers. https://github.com/terraform-linters/tflint
> 1 votes

---
Tags: terraform, linting
---


# Question
Title: Include verbose output in terraform/terragrunt
Is there any way to include verbose / debugging output in Terraform / Terragrunt?

I'm new to the language, and often struggle with knowing exactly what's going on. It would be helpful for me to be able to inspect certain values; e.g. to confirm that locals & variables have the values I expect, to get the ids of created components, etc.

I can include such info by creating outputs and assigning values there; but if I'm debugging a module I then have to include the output in the module's output, and again in the calling code's output for it to be visible; then have to delete those entries before delivering the solution.

I'd like a solution more like C#'s `Debug.WriteLine(/*...*/)` or PowerShell's `Write-Verbose #...` i.e. somewhere I can say what value I'd like to inspect, but have that output only show in the terminal when run with verbose output enabled, so these statements can be toggled on when needed, rather than impacting the delivered solution.

Does anything like this exist in the language? So far I've found nothing documented or by Googling.

Note: The `TF_LOG` environment variable documented here is helpful for providing more info on what's going on under the covers; but only allows you to see predefined output; not to push your own content to the output stream.

# Answer
> 1 votes
You might be able to do something with the local-exec Provisioner to push the information out to an external script or file which you could monitor separately during the run.

The issue you might encounter is the run order, HCL isn't processed top to bottom like a "traditional" programming language, it works out a dependency tree for the resources it is creating, so might not run the local-exec when you'd expect them to be run.

---
Tags: terraform, terraform-module, terraform-workspace
---


title: How to tag source code using gitlabCI

text:
# Question
Title: How to tag source code using gitlabCI
We got gitlab new to our environment and trying to create some CI CD pipelines using gitlab CI. I have got some better progress with running some maven goals, creating pipelines and archiving artifacts using my .gitlab-ci.yml as below. I tried to give give tags after script which doesnt work. Now, I am trying to figure out how can I automate git tags to my sourcecode. I would like to create tag for sourcecode after master branch got successful build. we are using Semantic versions to tag our source code. Finally, so whenever master got successful build I would like to create a tag to my master branch.

```
image: maven:3.5-jdk-8-alpine

stages:
 - build
 - deploy
 - tag

maven_build:
  stage: build

  script:
   - mvn clean package
artifacts:
 paths:
 - target/*.jar

after_script:
   - ls -a
   - cd target && ls -a
   - git --version
   - git tag -a 1.0.15 -m "Version created by gitlab-ci Build"
   - git push origin 1.0.15
only:
  - master

```

logs:

```
Running after script...
$ ls -a
.
..
.git
.gitignore
.gitlab-ci.yml
 LICENSE
README.md
pom.xml
src
target
$ cd target && ls -a
.
..
apidocs
classes
generated-sources
generated-test-sources
javadoc-bundle-options
maven-archiver
maven-simple-0.2-SNAPSHOT-javadoc.jar
maven-simple-0.2-SNAPSHOT-sources.jar
maven-simple-0.2-SNAPSHOT.jar
maven-status
surefire-reports
test-classes
Uploading artifacts...
target/maven-simple-0.2-SNAPSHOT-javadoc.jar: found 1 matching files
Uploading artifacts to coordinator... ok            id=60598296
responseStatus=201 Created token=Gu-eH1es

```

Job succeeded

# Answer
> 13 votes
I tried to change docker image and added couple steps for git tag. Below is the answer worked for me,

```
image: maven

stages:
- build
- deploy
- tag

maven_build:
stage: build

  script:
   - mvn clean package
   artifacts:
  paths:
   - target/*.jar

  after_script:
   - ls -a
   - cd target && ls -a
   - git --version
   - git remote remove origin
   - git remote set-url origin https://"username:passwd"@gitlab.com/accountname/projectname
   - git tag -a 1.0.15 -m "Version created by gitlab-ci Build"
   - git push origin 1.0.15
  only:
    - master

```

# Answer
> 3 votes
Combining the answer of https://devops.stackexchange.com/a/3854/10932 and the comment of Overbryd along with a slight modification worked for me. Here, instead of using using **git remote set-url** command, I used **git remote add**.

The code will look like this:

```
image: maven

stages:
- build
- deploy
- tag

maven_build:
stage: build

  script:
   - mvn clean package
   artifacts:
  paths:
   - target/*.jar

  after_script:
   - ls -a
   - cd target && ls -a
   - git --version
   - git remote remove origin
   - git remote add origin https://"username:passwd"@gitlab.com/accountname/projectname.git
   - git config user.email "${GITLAB_USER_EMAIL}"
   - git config user.name "${GITLAB_USER_NAME}"
   - git tag -a 1.0.15 -m "Version created by gitlab-ci Build"
   - git push origin 1.0.15
  only:
    - master

```

Thanks to both.

# Answer
> 2 votes
`git push origin 1.0.15` should work and a new tag should be added to the tags in gitlab

https://docs.gitlab.com/ee/university/training/topics/tags.html

> ```
> git checkout master
>
> # Lightweight tag
> git tag my_lightweight_tag
>
> # Annotated tag
> git tag -a v1.0 -m ‘Version 1.0’
> git tag
>
> git push origin --tags
>
> ```

# Answer
> 1 votes
If you prefer ssh connection use this:

```
after_script:
  - git --version
  - git remote remove origin
  - git remote add origin git@gitlab.com:$CI_PROJECT_PATH.git
  - git tag -a v-$CI_COMMIT_SHORT_SHA -m "Version created by gitlab-ci Build"
  - git push --tags

```

# Answer
> 0 votes
I find myself in the same situation, where after publishing a build of an artifact, I want to tag the commit with a version number tag.

One solution I'm considering is to create a service (either inside or outside of GitLab) that's capable of pushing a tag to any repository, but **can't do anything else**. The service itself would possess credentials allowing it to write to repositories, so that each project build doesn't need to be granted write access to the repo.

I like this solution because GitLab doesn't seem to offer a permission level that can push tags onto existing commits, without also granting the ability to push commits containing new content. Generic write access is more permission than we need or want for this use case, so if we had it, we'd probably need to limit the scope by creating a user or a token *for each repository* with permissions to commit to just that repository. Then we'd need to manage all those users or tokens over time, which seems like it could get unwieldy.

---
Tags: continuous-integration, gitlab, gitlab-ci-runner
---


# Question
Title: Why are variables with fixed values not visible in Jenkins Pipeline stage?
I'm new to DevOps, and am cobbling together a beginner understanding from lots of reading, YouTube videos, and online examples.

I've had initial success adapting this helpful tutorial. I.e. in my bitbucket project, I've introduced a webhook that posts to Jenkins' generic pipeline url. The script referenced in the video is here. My (simplified) adaptation is:

```
node {
 properties([
  pipelineTriggers([
   [$class: 'GenericTrigger',
    genericVariables: [
     [ key: 'committer_name', value: '$.actor.name' ],
     [ key: 'committer_email', value: '$.actor.emailAddress' ],
     [ key: 'ref', value: '$.changes[0].refId'],
     [ key: 'tag', value: '$.changes[0].ref.id', regexpFilter: 'refs/tags/'],
     [ key: 'commit', value: '$.changes[0].toHash' ],
     [ key: 'repo_slug', value: '$.repository.slug' ],
     [ key: 'project_key', value: '$.repository.project.key' ],
     [ key: 'clone_url', value: '$.repository.links.clone[0].href' ],
     [ key: 'var1', value: 'value1' ],
     [ key: 'var2', value: 'value2' ],
     [ key: 'var3', value: 'value3' ],
     [ key: 'var4', value: 'value4' ]
    ],

    causeString: '$committer_name pushed tag $tag to $clone_url referencing $commit',

    token: 'my_token',

    printContributedVariables: true,
    printPostContent: true,

    regexpFilterText: '$ref',
    regexpFilterExpression: '^refs/tags/.*'
   ]
  ])
 ])

 stage("Prepare") {
  deleteDir()
  sh '''
  echo $committer_name
  echo $committer_email
  echo $var1
  echo $var2
  '''
 }
}

```

Following is console output from one run of this job:

```
user pushed tag wip35 to https://bitbucket.someplace.com/scm/san/user_generic_webhook_sender.git referencing 24e71eaa1a9f92fd850135798a4177e810a926ab
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on builder-231 in /home/builder/workspace/on_this_project_generic_webhook_build_that_project
[Pipeline] {
GenericWebhookEnvironmentContributor
 Received:

{"eventKey":"repo:refs_changed","date":"2021-05-13T16:02:29-0700","actor":{"name":"user","emailAddress":"user@someplace.com","id":4464,"displayName":"user","active":true,"slug":"user","type":"NORMAL","links":{"self":[{"href":"https://bitbucket.someplace.com/users/user"}]}},"repository":{"slug":"user_generic_webhook_sender","id":4285,"name":"user_generic_webhook_sender","description":"Sends generic webbooks to  Jenkins","scmId":"git","state":"AVAILABLE","statusMessage":"Available","forkable":true,"project":{"key":"SAN","id":2280,"name":"sandbox_projects","description":"sandbox_projects_prj","public":true,"type":"NORMAL","links":{"self":[{"href":"https://bitbucket.someplace.com/projects/SAN"}]}},"public":false,"links":{"clone":[{"href":"https://bitbucket.someplace.com/scm/san/user_generic_webhook_sender.git","name":"http"},{"href":"ssh://git@bitbucket.someplace.com:7999/san/user_generic_webhook_sender.git","name":"ssh"}],"self":[{"href":"https://bitbucket.someplace.com/projects/SAN/repos/user_generic_webhook_sender/browse"}]}},"changes":[{"ref":{"id":"refs/tags/wip35","displayId":"wip35","type":"TAG"},"refId":"refs/tags/wip35","fromHash":"0000000000000000000000000000000000000000","toHash":"24e71eaa1a9f92fd850135798a4177e810a926ab","type":"ADD"}]}

Contributing variables:

    clone_url = https://bitbucket.someplace.com/scm/san/user_generic_webhook_sender.git
    commit = 24e71eaa1a9f92fd850135798a4177e810a926ab
    committer_email = user@someplace.com
    committer_name = user
    project_key = SAN
    ref = refs/tags/wip35
    repo_slug = user_generic_webhook_sender
    tag = wip35
    var1 =
    var2 =

[Pipeline] properties
[Pipeline] stage
[Pipeline] { (Prepare)
[Pipeline] deleteDir
[Pipeline] sh
+ echo user
user
+ echo user@someplace.com
user@someplace.com
+ echo

+ echo

[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

```

**Question: Why are `$var1` and `$var2` empty during the stage execution, whereas `$committer_name` and `$committer_email` have the values that were assigned in the `genericVariables` section of the Pipeline script?**

# Answer
> 1 votes
I've spent a bit of time playing "follow the bouncing ball" through the Generic Webhook Trigger Plugin source code, and it would seem that it is because those variables being displayed do not originate from your pipeline at all. Or at least, the values don't.

What happens is that when your webhook is triggered, the webhook request receiver grabs the trigger for your Jenkins job, which creates a cause object that will then take the list of generic variables you supplied and pass them to the variable resolver which will actually then use the post content parameter resolver to grab the value from the post content for any of the generic variables where you supplied either a JSON or XML path as the value. Where the value does not match an input from the request headers, request parameters, or POST content, the variable is silently discarded.

Since genericVariables are only used by the Generic Webhook Trigger, and not by the Jenkins Pipeline itself, if you want a static variable to remain in scope once you enter the stage you will need to define it in the proper context - i.e, using the Groovy def statement inside the node context, or use the environment

---
Tags: jenkins, jenkins-pipeline, pipeline
---


# Question
Title: Where is Jenkins Pipeline script stored and can I control it?
Several tutorials I'm referencing to learn about Jenkins Pipeline scripting guide one to create Jenkins Pipeline jobs as follows:

1. Click `New Item` in the left pane.
2. Click `Pipeline` and enter an item name.
3. Fill out the little `Pipeline` textbox with your script.

**Question: where does this script physically reside?** I don't see anywhere to save a filename -- am I creating `foo.sh` or `/path/to/bar.py` or `/somewhere/../else/baz.pl`? **What is the filename and realpath of the script that I type out in that tiny little text box?**

**Question: is it possible to control the name/location of the Pipeline script that I type in that textbox?** E.g. can this script be something version-controlled in some git repo? And specifically, when this job has no build triggers. I can't help but feel that anonymous script edited in that tiny little text box might be limiting for larger and more complicated scripts.

Sorry for the multiple questions; I've tried to limit them and keep their scope all related.

# Answer
> 1 votes
The good news, is that you absolutely don't have to use that little box to manage your pipeline script.

Jenkins also allows you to source your Jenkinsfile from an SCM repository like GitHub, and will even let you automagically import branches as separate jobs with separate Jenkinsfiles per branch using multibranch pipelines.

Pipeline scripts are in a language known as Groovy, not Perl or sh or similar. The definitive guides to Groovy are (of course) the Jenkins reference guide and the Groovy reference guide.

---
Tags: jenkins, jenkins-pipeline
---


# Question
Title: java.lang.UnsupportedOperationException: no known implementation of class jenkins.tasks.SimpleBuildWrapper is named BuildUser in jenkins
I preparing script in Jenkins as below where I getting error while build job. This job is send email to user for `input` for next step.

```
 [Pipeline] End of Pipeline
 java.lang.UnsupportedOperationException: no known implementation of class jenkins.tasks.SimpleBuildWrapper is named BuildUser
    at org.jenkinsci.plugins.structs.describable.DescribableModel.resolveClass(DescribableModel.java:549)
    at org.jenkinsci.plugins.structs.describable.DescribableModel.coerce(DescribableModel.java:473)
    ...
    ...

```

Version :

```
$ java --version
 openjdk 11.0.11 2021-04-20

jenkins : 2.277.3

```

Pipeline code:

```
def user
node {
wrap([$class: 'BuildUser']) {
user = env.BUILD_USER_ID
}

emailext mimeType: 'text/html',
             subject: "[Jenkins]${currentBuild.fullDisplayName}",
             to: "user@xxx.com",
             body: '''<a href="${BUILD_URL}input">click to approve</a>'''
}

pipeline {
agent any
stages {
    stage('deploy') {
        input {
            message "Should we continue?"
            ok "Yes"
        }
        when {
            expression { user == 'hardCodeApproverJenkinsId'}
        }
        steps {
            sh "echo 'describe your deployment' "
        }
    }
   }
}

```

Can anyone please review this, please ?

# Answer
Have you installed the Build User Vars Plugin? This plugin provides the "BuildUser" class.
> 1 votes

---
Tags: jenkins, automation, groovy, pipeline
---


title: Docker image file location

text:
# Question
Title: Docker image file location
I run Docker on Windows 10 machine with WSL 2 support. I would like to know where docker image files are located in this situation? Where docker image files are located in case when no WSL 2 is enabled.

# Answer
According to this blog post, Docker Desktop on Windows stores its containers *inside* another container, that container is stored in `C:\ProgramData\DockerDesktop`. If you `docker inspect` a container you have setup, it will give you a Linux path, which you can then get to once you go inside the host container using `docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -i sh`

This assumes you are using the default (Hyper-V) hypervisor for Docker Desktop. Where you use VMware as the hypervisor instead, different rules may apply.
> 1 votes

---
Tags: docker
---


# Question
Title: Why does Jenkins Pipeline script returnStatus value not match status examined in shell?
This is my little newbie Groovy script, trying to cobble together a very beginner understanding of Jenkins Pipelines:

```
node {
 stage("hello") {
  def var = "val"
  echo "${var}"
  def stdout = sh( script: 'pwd', returnStdout: true ).trim()
  echo "${stdout}"
  def ret_status = sh( script: 'cd subdir', returnStatus: true )
  echo "${ret_status}"
 }
}

```

The output is:

```
Started by user unknown or anonymous
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /home/user/workspace/newbie_dont_know_what_hes_doin
[Pipeline] {
[Pipeline] stage
[Pipeline] { (hello)
[Pipeline] echo
val
[Pipeline] sh
+ pwd
[Pipeline] echo
/home/user/workspace/newbie_dont_know_what_hes_doin
[Pipeline] sh
+ cd subdir
/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/script.sh: 1: cd: can't cd to subdir
[Pipeline] echo
2
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

```

The failure to `cd subdir` is intentional -- to demonstrate the mechanics of capturing shell stdout and status.

**Why does the failed `cd subdir` report a returnStatus of `2` when `cd`ing to nonexistent directories in `bash` returns status-code `1`?** The Groovy documentation I have as reference says `sh` runs a Bourne shell script.

```
$ echo $SHELL
/bin/bash
$ cd subdir
-bash: cd: subdir: No such file or directory
$ echo $?
1
$

```

# Answer
There's a few things to note. First is that in the absense of a shebang being supplied, the actual interpreter that will be used is actually "sh", not "bash".

Taking a look at the Jenkins source for the BourneShellScript task, what happens is that Jenkins does not actually return the exit code from the subshell - it monitors a file called "jenkins-result.txt for changes (using a FileMonitoringTask) and when that file has been updated, it merely assigns the content of it as the exit status.

From what I can see, when you call "sh" it does not just run your script code, but actually wraps it in a fairly substantial block of boilerplate code (refer line 272 of the above linked BourneShellScript). In effect, something closer to this:

```
sh -c ({ while [ -d '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0' -a \\! -f '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/jenkins-result.txt' ]; do touch '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/jenkins-log.txt'; sleep 3; done } & jsc=durable-2fa119e0; JENKINS_SERVER_COOKIE=$jsc sh -xe '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/script.sh' > '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/jenkins-output.txt' 2> 'jenkins-log.txt'; echo $? > '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/jenkins-result.txt.tmp'; mv '/home/user/workspace/newbie_dont_know_what_hes_doin@tmp/durable-2fa119e0/jenkins-result.txt.tmp' '/home/user/workspace/newbie_dont_know_what_hes_doin\@tmp/durable-2fa119e0/jenkins-result.txt'; wait) >&- 2>&- &

```

This is likely necessary to support scenarios where the node on which the script is executed is not the node on which the pipeline is executing, however it does leave a lot of room for error in returning the result, depending on the idiosyncracies of whatever interpreter "sh" is aliased to (not necessarily bash) and the OS on which it is running.
> 4 votes

---
Tags: jenkins, jenkins-pipeline, groovy, bash
---


# Question
Title: How to ensure Nodes are killed when Job is finished using Cluster Autoscaler in Kubernates
I have large Kubernates jobs which take the whole resources of one node / droplet in Digitalocean (it's not running on AWS).

Using resources in a deploy.yaml, the cluster autoscaler works to scale up, but sometimes the nodes continue to run instead of killing / scaling down when the job is done.

How do I make sure the nodes get killed when the job is done? They are expensive!

# Answer
> 1 votes
When a Job completes, no more Pods are created, but the Pods are not deleted either.

https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup

1. clear it by another cronjob.
2. use operator to wrapper it and add remove mechanism in the operator.

---
Tags: kubernetes
---


# Question
Title: How to change ownership of folder inside the container after build?
I have this service in my `docker-compose.yml`

php-fpm: container\_name: php\_app restart: always build: context: . dockerfile: ./docker/php-fpm/Dockerfile volumes: \- ./src:/var/www/html

that take everything of the host folder `src` and add to `/var/www/html`:

```
FROM php:8.0.2-fpm-alpine
WORKDIR /var/www/html

RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install mysqli
RUN apk add icu-dev

COPY docker/php-fpm/config/php.ini /usr/local/etc/php/

RUN apk add --update nodejs nodejs-npm
RUN npm install gulp-cli -g
RUN npm install

COPY src src/

CMD ["php-fpm"]

EXPOSE 9000

```

accessing the container using `sudo docker exec -it php_app sh`  and executing `ls -la` I have:

drwxrwxr-x 6 1001 1001 4096 May 12 15:54 writable

which must be owned by `www-data`, how can I set the ownership automatically when the builds complete?

# Answer
you need to create the www-data user in the dockerfile eg

```
RUN addgroup -g 1000 www-data && adduser -G www-data -g www-data -s /bin/sh -D www-data

RUN chown -R www-data:www-data /home/www-data && \
    chown -R www-data:www-data /var/www

```
> 1 votes

---
Tags: docker, docker-compose
---


# Question
Title: Which container will be targeted when replicas larger than 1
Below is a simplified version of my `docker-compose.yml` file.

I can understand or find any resources to tell me which container of the 5 `django` replicas the nginx container will target if i reference it.

For example, as you can see if my compose file I have one instance of Nginx and five of Django.

If my nginx config i have an upstream like this:

```
upstream docking_django {
    server web:3000;
}

```

Which web container will the upstream come from? Will it load balance between them? I'm deploying to AWS ECS and when I try running it, it seems like it's load balancing somehow. But I would like to understand it further since it doesn't seem to behave the same way always.

```
version: '3.9'

services:
  web:
    image: .dkr.ecr.eu-west-3.amazonaws.com/tin-api-v2/django:${IMAGE_TAG}
    build:
      context: .
      dockerfile: ./Dockerfile
    networks:
      - demoapp
    deploy:
      replicas: 5

  nginx:
    image: .dkr.ecr.eu-west-3.amazonaws.com/tin-api-v2/nginx:${IMAGE_TAG}
    build:
      context: .
      dockerfile: ./nginx.Dockerfile
    ports:
      - 80:80
    depends_on:
      - web
    networks:
      - demoapp
    deploy:
      replicas: 1

networks:
  demoapp:

```

# Answer
Yes Felix. Loosely speaking it "load balance" across your 5 web container instances. If you are using the ECS / Compose integration and you are bringing up this stack with a `compose` up this is roughly what happens:

* it creates an ECS cluster
* it creates task def for the `web` task and the `nginx` task
* it creates a service for `web` (with 5 tasks in it)
* it creates a service for `nginx` (with 1 task in it)
* it exposes the `nginx` service via an ALB to the public (because you expose the port 80)
* it creates an internal mechanism based on ECS Service Discovery to be able to resolve `web` inside the cluster (that's how the `nginx` is able to reach an entity called `web`).

ECS SD leverages CloudMap/Route53 which will round-robin across the 5 entries (`web` tasks). You can read more here.
> 1 votes

---
Tags: docker, nginx, amazon-ecs
---


# Question
Title: 503 Service Temporarily Unavailable using Kubernetes
I am having some issue with creating ingress for a nginx service that I deployed in a kubernetes cluster. I am able to open the web page using port forwarding, so I think the service should work.The issue might be with configuring the ingress.I checked for selector, different ports, but still could not find where goes wrong. Anyone could help ? Thank you in advance.

```
# Source: django/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: vehement-horse-django-config
  labels:
    app.kubernetes.io/name: django
    helm.sh/chart: django-0.0.1
    app.kubernetes.io/instance: vehement-horse
    app.kubernetes.io/managed-by: Tiller
    enter code here

data:
---
# Source: django/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: vehement-horse-django
  labels:
    app.kubernetes.io/name: django
    helm.sh/chart: django-0.0.1
    app.kubernetes.io/instance: vehement-horse
    app.kubernetes.io/managed-by: Tiller
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
      name:
  selector:
    app.kubernetes.io/name: django
    app.kubernetes.io/instance: vehement-horse
---
# Source: django/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vehement-horse-django
  labels:
    app.kubernetes.io/name: django
    helm.sh/chart: django-0.0.1
    app.kubernetes.io/instance: vehement-horse
    app.kubernetes.io/managed-by: Tiller
spec:
  replicas:
  selector:
    matchLabels:
      app.kubernetes.io/name: django
      app.kubernetes.io/instance: vehement-horse
  template:
    metadata:
      labels:
        app.kubernetes.io/name: django
        app.kubernetes.io/instance: vehement-horse
    spec:
      containers:
        - name: django
          image: "website:72cf09525f2da01706cdb4c6131e8bf1802ea6a3"
          imagePullPolicy: Always
          envFrom:
          - configMapRef:
              name: vehement-horse-django-config
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          resources:
            limits:
              cpu: 1
              memory: 1Gi
            requests:
              cpu: 1
              memory: 512Mi

      imagePullSecrets:
        - name: some-secret
---
# Source: django/templates/ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: vehement-horse-django
  labels:
    app.kubernetes.io/name: django
    helm.sh/chart: django-0.0.1
    app.kubernetes.io/instance: vehement-horse
    app.kubernetes.io/managed-by: Tiller
spec:
  rules:
    - host: something.com
      http:
        paths:
          - path: /
            backend:
              serviceName: vehement-horse-django
              servicePort: http

```

# Answer
i'm guessing the problem is that the name of the service port is not configured, and in the ingress you specified the http name and it can not find it. name your port http , or give the port that you want to use (80) to the servicePort in the ingress file
> 3 votes

---
Tags: kubernetes, helm
---


# Question
Title: This job is stuck because you don't have any active runners online
I try to use Gitlab-CI and create the runner, but when jobs start, the job with the needed runner doesn't start:

But this runner exists in the list:

How to use a runner?

And an additional question: all works fine with the "automatic" runner when I only create `.yml` file. Why do I need to create some other runners and why runners needed?

# Answer
Runners are machines (typically containers) where your jobs run. Gitlab offers shared runners, and you can also bring in your own runners (machines that you register against Gitlab to run your jobs).

The main difference between Gitlab shared runners and the ones you bring is that the shared runners are, well, shared, so sometimes you have to wait for a bit for a job to start. You can also bring more powerful machines so that your jobs run faster. You can find some docs on runners here.

In your case it seems that the runners have a problem, looking at the exclamation sign next to the name, which should be a green ball. Maybe hovering over the sign will give you some clues into what's happening.
> 3 votes

# Answer
After Further research, I found an open issue on gitlab pretty similar to mine. They describe runners freshly created and unable to pick up jobs. As for my use case, The runner was created but with warning stating "New Runner, has not connected yet".

In order to further check the status of the runner, I went to my terminal on the machine where I installed and registered the runner, namely my mac book pro, and I ran the following command:

```
sudo gitlab-runner verify

```

In the settings of the project, the runner's status switches to "**Online**" as you can see below, with a green label:

As a result, the project's pipeline status went from "Pending" to "Running"

Nevertheless, while the change of pipeline status, from "pending" to "running" thanks to gitlab runner's status going "Online", the associated CI build Job Status is still stuck at "Pending". This means that gitlab runner, even with all its characteristics looking fine, fails to pick up the job he's supposed to.

I'm aware this is not really a pure answer with final solution but I hope this will help move things forward on this topic !
> 3 votes

---
Tags: gitlab-ci-runner
---


# Question
Title: Nginx-Ingress Controller are compatible with Network Security Policy or not?
I have created one application where I use **Nginx-Ingress Controller version 3.29.0**. Without using **network security policy** I'm able to access my application. But, after applying network security policy, I'm unable to access my application. I checked all logs and troubleshoots the whole application but there is nothing anything odd. all my services and nginx ingress controller are working fine. But still when I go to access my application it will show me error that, **"To long to respond"**.

Can any one help me with this? **Is there anything like nginx-ingress controller is not compatible with network security policies or what? and how can I solve this?**

Nginx-Ingress Controller version - 3.29.0 Kubernetes version - 1.19.7

Thank you.

# Answer
I don't think it is the compatibility, need to really see your configuration to be sure. But most likely, you block something wrongly. If you use the default network policy api `networking.k8s.io/v1` notice that it only allows what is defined.
> 1 votes

---
Tags: azure-devops, nginx, ingress, azure-kubernetes-service
---


title: Using `dig` Ansible module?

text:
# Question
Title: Using `dig` Ansible module?
I am trying to look up the IPv4 address (a single one, which I will `assert` on!) for a given name using Ansible and use that as a fact "down the line".

An MWE looks like this:

```
---
# vim: set autoindent smartindent tabstop=2 shiftwidth=2 softtabstop=2 expandtab filetype=ansible:

- name: Preparations
  hosts: localhost
  connection: local
  vars:
    host2lookup: "example.com"
  tasks:
    - getent:
        database: ahostsv4
        key: "{{host2lookup}}"
    - debug:
        var: getent_ahostsv4
    - name: Lookup for {{host2lookup}}
      debug:
        msg: "{{lookup('dig', '{{host2lookup}}./A')}}"

```

... and fails as follows (relevant excerpt):

```
  localhost failed | msg: An unhandled exception occurred while running the lookup plugin 'dig'. Error was a <class 'ansible.errors.AnsibleError'>, original message: The dig lookup requires the python 'dnspython' library and it is not installed

```

So I thought to myself I'd install the `dnspython` library for the appropriate Python version, making the playbook look as follows:

```
---
# vim: set autoindent smartindent tabstop=2 shiftwidth=2 softtabstop=2 expandtab filetype=ansible:

- name: Preparing Ansible host
  hosts: localhost
  connection: local
  tasks:
    - name: Install dnspython package on Ansible host
      block:
        - name: Installing python-dnspython package
          apt:
            pkg:
              - python-dnspython
            update_cache: yes
            install_recommends: no
            state: present
          when: ansible_python.version.major == 2
        - name: Installing python3-dnspython package
          apt:
            pkg:
              - python3-dnspython
            update_cache: yes
            install_recommends: no
            state: present
          when: ansible_python.version.major == 3
      become: true
- name: Preparations
  hosts: localhost
  connection: local
  vars:
    host2lookup: "example.com"
  tasks:
    - getent:
        database: ahostsv4
        key: "{{host2lookup}}"
    - debug:
        var: getent_ahostsv4
    - name: Lookup for {{host2lookup}}
      debug:
        msg: "{{lookup('dig', '{{host2lookup}}./A')}}"

```

But the ultimate failure remains the same. Either that error is misleading or something else is fishy ...

```
Executing playbook dbg.yml

- Preparing Ansible host on hosts: localhost -
Gathering Facts...
  localhost ok
Installing python-dnspython package...
Installing python3-dnspython package...
  localhost ok

- Preparations on hosts: localhost -
Gathering Facts...
  localhost ok
getent...
  localhost ok
debug...
  localhost ok: {
    "changed": false,
    "getent_ahostsv4": {
        "93.184.216.34": [
            "RAW"
        ]
    }
}
Lookup for example.com...
  localhost failed | msg: An unhandled exception occurred while running the lookup plugin 'dig'. Error was a <class 'ansible.errors.AnsibleError'>, original message: The dig lookup requires the python 'dnspython' library and it is not installed

- Play recap -
  localhost                  : ok=5    changed=0    unreachable=0    failed=1    rescued=0    ignored=0

```

What other than the appropriate `dnspython` package am I missing here?

---

The `dnsutils` package (containing `dig`) and its library dependencies is installed. Adding these as prerequisites into the `apt:` task therefore makes no difference.

Found another smoking gun, though:

```
$ python3 -m dnspython
/usr/bin/python3: No module named dnspython
$ sudo -H pip3 install dnspython
Requirement already satisfied: dnspython in /usr/lib/python3/dist-packages

```

I stand corrected. As per the examples page I should import something like `dns.query` (`python3 -m dns.query`). And that succeeds.

My `ansible.cfg` is this:

```
$ cat ansible.cfg
[defaults]
nocows = true
command_warnings = false
stdout_callback = unixy
display_skipped_hosts = false
interpreter_python=auto

```

The whole thing runs on Ubuntu 18.04.

---

As per request in a comment, I am providing more output. This is still on Ubuntu 18.04. With the (above shown) `ansible.cfg` in place, I get the following output:

```
$ ansible localhost -m setup -a 'filter=ansible_python*'
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_python": {
            "executable": "/usr/bin/python2",
            "has_sslcontext": true,
            "type": "CPython",
            "version": {
                "major": 2,
                "micro": 17,
                "minor": 7,
                "releaselevel": "final",
                "serial": 0
            },
            "version_info": [
                2,
                7,
                17,
                "final",
                0
            ]
        },
        "ansible_python_version": "2.7.17"
    },
    "changed": false
}

```

However, `ansible-playbook` behaves differently here. Consider the following playbook:

```
---

- hosts: all
  tasks:
    - debug:
        var: ansible_python
    - debug:
        var: ansible_python_version

```

The output becomes:

```
$ ansible-playbook -i localhost, -c local dbg.yml
Executing playbook dbg.yml

- all on hosts: all -
Gathering Facts...
  localhost ok
debug...
  localhost ok: {
    "ansible_python": {
        "executable": "/usr/bin/python3",
        "has_sslcontext": true,
        "type": "cpython",
        "version": {
            "major": 3,
            "micro": 9,
            "minor": 6,
            "releaselevel": "final",
            "serial": 0
        },
        "version_info": [
            3,
            6,
            9,
            "final",
            0
        ]
    },
    "changed": false
}
debug...
  localhost ok: {
    "ansible_python_version": "3.6.9",
    "changed": false
}

- Play recap -
  localhost                  : ok=3    changed=0    unreachable=0    failed=0    rescued=0    ignored=0

```

... **comment out the following line** from `ansible.cfg`:

```
interpreter_python=auto

```

... and the output (when rerunning the previous command) becomes:

```
$ ansible-playbook -i localhost, -c local dbg.yml
Executing playbook dbg.yml

- all on hosts: all -
Gathering Facts...
  localhost ok
debug...
  localhost ok: {
    "ansible_python": {
        "executable": "/usr/bin/python",
        "has_sslcontext": true,
        "type": "CPython",
        "version": {
            "major": 2,
            "micro": 17,
            "minor": 7,
            "releaselevel": "final",
            "serial": 0
        },
        "version_info": [
            2,
            7,
            17,
            "final",
            0
        ]
    },
    "changed": false
}
debug...
  localhost ok: {
    "ansible_python_version": "2.7.17",
    "changed": false
}

- Play recap -
  localhost                  : ok=3    changed=0    unreachable=0    failed=0    rescued=0    ignored=0

```

Nothing in the documentation suggests that this should happen.

# Answer
> 2 votes
See:

See also Setting the Python interpreter that explains various configurations' options, variables, and debugging.

---

Q: ***"None of that explains why - and that is/was my point - **ansible\_python wrongly claims to be running on Python 3.x on the local node**. Because all I do by setting **interpreter\_python** in ansible.cfg is to give Ansible free rein on how it interprets things."***

Short answer: You can't configure the version of Python the Ansible utils use ($ ansible --version). INTERPRETER\_PYTHON configures the Python version used by the Ansible modules on the remote hosts.

A: Configuration option INTERPRETER\_PYTHON works as expected. There is nothing wrong when Python3 is discovered when asked by `interpreter_python=auto`.

See implementation details of the function discover\_interpreter. When the version of the distro is not in \_INTERPRETER\_PYTHON\_DISTRO\_MAP

> *"return nearest previous version we're newer than"*

```
def _version_fuzzy_match(version, version_map):
...
    # slot match; return nearest previous version we're newer than
    kpos = bisect.bisect(sorted_looseversions, find_looseversion)

```

```
interpreter_python=auto

```

```
shell> ansible localhost -m setup -a 'filter=ansible_python_version'
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_python_version": "3.7.3",
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false
}

```

> *"The default value of **auto\_legacy** provides all the same behavior, but for backward compatibility with older Ansible releases that always defaulted to **/usr/bin/python**, will use that interpreter if present (and issue a warning that the default behavior will change to that of auto in a future Ansible release."*

```
interpreter_python=auto_legacy

```

```
shell> ansible localhost -m setup -a 'filter=ansible_python_version'
[DEPRECATION WARNING]: Distribution Ubuntu 19.04 on host localhost should use /usr/bin/python3, but is using /usr/bin/python for backward compatibility with prior Ansible releases. A future
 Ansible release will default to using the discovered platform python for this host. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more
information. This feature will be removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_python_version": "2.7.16",
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}

```

See the notes and examples below.

---

Q: ***"What does the ansible command line tool have to do with the behavior I see with ansible-playbook?"***

A: Both ansible command line tool and ansible-playbook will report the same value of *ansible\_python\_version*. For example,

```
shell> ansible localhost -m setup | grep ansible_python_version
        "ansible_python_version": "2.7.16",

```

```
shell> cat playbook.yml
- hosts: localhost
  tasks:
    - debug:
        var: ansible_python_version

```

```
shell> ansible-playbook playbook.yml

ok: [localhost] => {
    "ansible_python_version": "2.7.16"
}

```

---

Notes 1. INTERPRETER\_PYTHON configuration parameter says:

> Path to the Python interpreter to be used for module execution **on remote targets**

See How to choose a python interpreter for Ansible playbook?

2. Ansible will tell what Python is used **on master**

```
shell> ansible --version
ansible 2.9.2
  config file = /home/vlado/.ansible.cfg
  configured module search path = [u'/home/vlado/.ansible/my_modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.16 (default, Oct  7 2019, 17:36:04) [GCC 8.3.0]

```
3. dig says

> The below requirements are needed **on the local master node** that executes this lookup: dnspython

4. It's possible to install both versions

```
shell> dpkg -l | grep dnspython
ii  python-dnspython         1.16.0-1 all          DNS toolkit for Python
ii  python3-dnspython        1.16.0-1 all          DNS toolkit for Python 3

```
5. See Ansible packages in Ubuntu what version of Python was used.
6. ansible\_python\_interpreter works as expected

**On master(Ubuntu) the playbook**

```
shell> cat playbook.yml
- hosts: test_01
  tasks:
    - getent:
        database: hosts

```

```
shell> cat hosts
test_01 ansible_python_interpreter=/usr/local/bin/python3.6

```

```
shell> ansible-playbook playbook.yml -i hosts -vvv | grep python

```

gives

> ansible python module location = /usr/lib/python2.7/dist-packages/ansible **python version = 2.7.16** (default, Oct 7 2019, 17:36:04) \[GCC 8.3.0\]

...

> \<test\_01\> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="admin"' -o ConnectTimeout=10 -o ControlPath=/export/home/vlado.config/.ansible/cp/5a3ab05cf7 -tt test\_01 '/bin/sh -c '"'"'sudo -H -S -n -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-xnvwkdksdqqoehvrgebdgcnrzcrmoszv ; **/usr/local/bin/python3.6** /home/admin/.ansible/tmp/ansible-tmp-1576667662.62-67468482045094/AnsiballZ\_getent.py'"'"'"'"'"'"'"'"' && sleep 0'"'"''

* ansible-playbook **on master** runs with `python version = 2.7.16`
* module **on remote** (test\_01) runs with `/usr/local/bin/python3.6`

**Change ansible\_python\_interpreter**

```
shell> cat hosts
test_01 ansible_python_interpreter=/usr/local/bin/python2.7

```

the same playbook gives

> ansible python module location = /usr/lib/python2.7/dist-packages/ansible **python version = 2.7.16** (default, Oct 7 2019, 17:36:04) \[GCC 8.3.0\]

...

> \<test\_01\> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="admin"' -o ConnectTimeout=10 -o ControlPath=/export/home/vlado.config/.ansible/cp/5a3ab05cf7 -tt test\_01 '/bin/sh -c '"'"'sudo -H -S -n -u root /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-fidpgwptggdoizirumvjmzuorxqawypu ; **/usr/local/bin/python2.7** /home/admin/.ansible/tmp/ansible-tmp-1576668475.22-210929624017605/AnsiballZ\_getent.py'"'"'"'"'"'"'"'"' && sleep 0'"'"''

* ansible-playbook **on master** runs with `python version = 2.7.16`
* but module **on remote** (test\_01) runs with `/usr/local/bin/python2.7` as requested

# Answer
> 1 votes
Since this confused the hell out of me and cost me about an hour to figure out, I am going to post an answer.

As you can see I am using `ansible_python` to determine the Python version and install the appropriate version of `dnspython`. Since this is on Ubuntu 18.04 it defaults to `python3`. I am suppressing the respective warning with `interpreter_python=auto` in the `ansible.cfg` inside the same folder.

But here's the bummer. Apparently `connection: local` somehow violates the rule about using `python3` whose `dnspython` package is installed. It made sense once I looked for `dig.py` (hoping for the Ansible lookup module of the same name). And indeed I found it:

```
$ locate dig.py
/usr/lib/python2.7/dist-packages/ansible/plugins/lookup/dig.py
/usr/lib/python2.7/dist-packages/ansible/plugins/lookup/dig.pyc

```

... alas, this means my version of Ansible uses Python 2.7 and so the `dig` module won't be able to make use of the `python3-dnspython` (Debian) package which I installed.

Still puzzling. But with the cause determined, I can work around it now.

# Answer
> 0 votes
In ansible, `lookup`'s are executed locally where ansible runs, not on the target server.

So you will have to ensure that `dnspython` is installed on your ansible machine.

---
Tags: ansible
---


# Question
Title: How to get traefik ingress to load balance across several ClusterIP services
I want to be able to load balance several nodes of a backend service represented by `ClusterIP` services using a `traefik.containo.us/v1alpha1` ingress. I know that `ClusterIP` service can load balance easily with deployments and `ReplicaSet` but the way this application is designed each node needs to be it's own Deployment as it is a stateful node.

So I either need a way to have the flow be:

```
Ingress --> [ClusterIP-Node1, ClusterIP-Node2, ClusterIP-Node3, ClusterIP-Node4]

```

or

```
Ingress --> ClusterIP-LoadBalancer --> [ClusterIP-Node1, ClusterIP-Node2, ClusterIP-Node3, ClusterIP-Node4]

```

How can I use `traefik.containo.us/v1alpha1` to automatically loadbalance between these `ClusterIP` services?

For example, if I currently have one `Ingress` for each \`ClusterIP, like this:

```
...
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: my-service-node1-ingress
spec:
  entryPoints:
    - web
    - websecure
  routes:
    - kind: Rule
      match: Host(`myservice-node1.mycompany.example`)
      services:
        - kind: Service
          name: myservice-node1-clusterip
          passHostHeader: true
          port: 80

---
apiVersion: v1
kind: Service
metadata:
  name: myservice-node1-clusterip
  labels:
    app: myservice
    tier: backend
    name: myservice-node1
spec:
  selector:
    app: myservice
    tier: backend
    name: myservice-node1-deployment
  type: ClusterIP
  ports:
    - name: rpc-listener
      protocol: TCP
      port: 80
      targetPort: 8545
... N+1

```

And I want to change that to a single `Ingress` Point similar to this:

```
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: my-service-ingress
spec:
  entryPoints:
    - web
    - websecure
  routes:
    - kind: Rule
      match: Host(`myservice.mycompany.example`)
      services:
        - kind: Service
          name: myservice-node1-clusterip
          passHostHeader: true
          port: 80

```

Is it possible to have the `traefik.containo.us/v1alpha1` load balance across the services similar to this? I saw something that looked promising on this page in the docs that you can have `n` number of `services[n].name` for a matching route `routes[n].services`. Does that mean you can do something similar to the following?

```
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: my-service-ingress
spec:
  entryPoints:
    - web
    - websecure
  routes:
    - kind: Rule
      match: Host(`myservice.mycompany.example`)
      services:
        - kind: Service
          name: myservice-node1-clusterip
          passHostHeader: true
          port: 80
        - kind: Service
          name: myservice-node2-clusterip
          passHostHeader: true
          port: 80
        - kind: Service
          name: myservice-node3-clusterip
          passHostHeader: true
          port: 80
        - kind: Service
          name: myservice-node4-clusterip
          passHostHeader: true
          port: 80

```

Also, is there a way to pass the `sessionAffinity` with it like there are in services `sessionAffinity: ClientIP`?

# Answer
> 1 votes
The sample I posted initially works as is:

```
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: my-service-ingress
spec:
  entryPoints:
    - web
    - websecure
  routes:
    - kind: Rule
      match: Host(`myservice.mycompany.example`)
      services:
        - kind: Service
          name: myservice-node1-clusterip
          passHostHeader: true
          port: 80
        - kind: Service
          name: myservice-node2-clusterip
          passHostHeader: true
          port: 80
        - kind: Service
          name: myservice-node3-clusterip
          passHostHeader: true
          port: 80
        - kind: Service
          name: myservice-node4-clusterip
          passHostHeader: true
          port: 80

```

---
Tags: kubernetes, traefik
---


# Question
Title: ingrex-nginx in kubernetes with express node.js does not return req.currentUser
This is currentUser middleware:

```
    export const currentUser = (
      req: Request,
      res: Response,
      next: NextFunction
    ) => {

      if (!req.session?.jwt) {
        return next();
      }
      try {
        const payload = jwt.verify(
          req.session.jwt,
          process.env.JWT_KEY!
        ) as UserPayload;
        console.log("payload in curretnUser middleware", payload);
        req.currentUser = payload;
      } catch (err) {
        console.log(
          "error in currentUser middleware in common module",
          err.message
        );
      }
      next();
    };

```

this is payload object that logged:

```
      payload in curretnUser middleware {
      id: '609b356f99a625001a76e65e',
      email: 'test@test.com',
      iat: 1620784495
   }

```

this is the router in "auth" service:

```
router.get(
  "/api/users/currentuser",
  currentUser,
  (req: Request, res: Response) => {
    // if req.currentUser is not defined it will send undefined
    console.log("request to api/users/currentuser", req.currentUser);
    res.send({ currentUser: req.currentUser || null });
  }
);

```

When request hits this route, this gets logged:

```
request to api/users/currentuser {
     currentUser: {
     id: '609b356f99a625001a76e65e',
     email: 'test@test.com',
     iat: 1620784495
   }
 }

```

How ever when I send POST request through postman I get `currentUser:null`. Somehow kubernetes or ingress-nginx is not attaching "currentUser" to request object and I could not figure out why. This is the nginx-ingress config:

```
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-srv
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.org/client-max-body-size: "4m"
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/auth-method: POST
spec:
  defaultBackend:
    service:
      name:
        nginx-ingress-default-backend
        # name: default-http-backend:80
      port:
        number: 80

  rules:
    - host: ticketing.dev
      http:
        paths:
          - path: /api/users/?(.*)
            pathType: Exact
            backend:
              service:
                name: auth-srv
                port:
                  number: 3000

```

# Answer
Try using this:- use-forwarded-headers

```
apiVersion: v1
kind: ConfigMap
data:
  use-forwarded-headers: "true"
metadata:
  name: nginx-configuration
  namespace: default

```
> 1 votes

---
Tags: kubernetes, node.js, ingress
---


# Question
Title: Kubernetes serve static content with ingress
I have a simple Django application and on a regular EC2 instance nginx is a reverse proxy serving static files and proxying requests to gunicorn/wsgi backend app.

Should I have a separate deployment and service for nginx which will be able to access static data on a volume to serve it as well as proxy requests to django pods or

Is it possible to configure ingress to serve static data without extra nginx deployment?

# Answer
You can't configure your Ingress to serve static data (from your host files for example) alone because the Ingress resource is just a way to configure a way of understanding for Kubernetes on how to access a specific internal resource from outside the cluster.

> An API object that manages external access to the services in a cluster, typically HTTP. Ingress may provide load balancing, SSL termination and name-based virtual hosting. -- Ingress | Kubernetes - https://kubernetes.io/docs/concepts/services-networking/ingress/

You must configure a web server (NGINX, Apache, etc.) with a mounted volume bound to your files. You'll also need a Service resource to publish the web server access internally. Then the Ingress resource will do the work.
> 5 votes

---
Tags: kubernetes, nginx, ingress, eks, kubernetes-volume
---


# Question
Title: How to allow a non-root user to write to a mounted EFS in EKS
I am having trouble configuring a statically provisioned EFS such that multiple pods, which run as a non-root user, can read and write the file system.

I am using the AWS EFS CSI Driver. My version info is as follows:

```
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.18", GitCommit:"6f6ce59dc8fefde25a3ba0ef0047f4ec6662ef24", GitTreeState:"clean", BuildDate:"2021-04-15T03:31:30Z", GoVersion:"go1.13.15", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18+", GitVersion:"v1.18.9-eks-d1db3c", GitCommit:"d1db3c46e55f95d6a7d3e5578689371318f95ff9", GitTreeState:"clean", BuildDate:"2020-10-20T22:53:22Z", GoVersion:"go1.13.15", Compiler:"gc", Platform:"linux/amd64"}

```

I followed the example from the github repo (https://github.com/kubernetes-sigs/aws-efs-csi-driver/tree/master/examples/kubernetes/multiple\_pods) updating the volumeHandle appropriately. The busybox containers defined in the specs for the example are able to read and write the file system, but when I add the same PVC to a pod which does not run as the root user the pod is unable to write to the mounted EFS. I have tried a couple other things to get this working as I expected it to:

None of these configurations allowed a non-root user to write to the mounted EFS. What am I missing in terms of configuring a statically provisioned EFS so that multiple pods, all of which run as a non-root user, can read and write in the mounted EFS?

For reference here are the pod definitions:

```
apiVersion: v1
kind: Pod
metadata:
  name: app1
spec:
  containers:
  - name: app1
    image: busybox
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out1.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: efs-claim
---
apiVersion: v1
kind: Pod
metadata:
  name: app2
spec:
  containers:
  - name: app2
    image: busybox
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out2.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: efs-claim
---
apiVersion: v1
kind: Pod
metadata:
  name: app3
spec:
  containers:
  - name: app3
    image: busybox
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out3.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  securityContext:
    runAsUser: 1000
    runAsGroup: 1337
    fsGroup: 1337
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: efs-claim

```

And the SC/PVC/PV:

```
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: efs-sc
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: efs-pv
  annotations:
    pv.beta.kubernetes.io/gid: {{ .Values.groupId | quote }}
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: efs-sc
  csi:
    driver: efs.csi.aws.com
    volumeHandle: fs-asdf123

```

# Answer
If anyone comes across this later I resolved my issue by using an `initContainer` for any Pod that needed to write to the file system.

For example:

```
apiVersion: v1
kind: Pod
metadata:
  name: app3
spec:
  containers:
  - name: app3
    image: busybox
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out3.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
    securityContext:
      runAsGroup: 1337
  initContainers:
  - name: fs-owner-change
    image: busybox
    command:
    - chown
    - "root:1337"
    - "/efs-fs"
    volumeMounts:
    - mountPath: /efs-fs
      name: persistent-storage
  securityContext:
    fsGroup: 1337
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: efs-claim

```

The rest of the definitions match what was in my question.

---

Working sort of off what @JerryChen suggested ("use access points") I discovered that things were simpler if I just used a dynamically provisioned EFS which does utilize EFS access points to allow shared access to an EFS instance. Below is the StorageClass, PersistentVolumeClaim, and an example Pod.

```
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com
parameters:
  provisioningMode: efs-ap
  fileSystemId:  {{ .Values.efsVolumeHandle }}
  directoryPerms: "775"
reclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: efs-sc
  resources:
    requests:
      storage: 5Gi  # Not actually used - see https://aws.amazon.com/blogs/containers/introducing-efs-csi-dynamic-provisioning/
---
apiVersion: v1
kind: Pod
metadata:
  name: app3
spec:
  containers:
  - name: app3
    image: busybox
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out3.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  securityContext:
    runAsUser: 1000
    runAsGroup: 1337
    fsGroup: 1337
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: efs-claim

```

Note the `directoryPerms` (775) specified in the StorageClass, as well as the `runAsGroup` and `fsGroup` specified in the Pod. When utilizing this PVC in a Pod that runs as a non-root user shared a user group number is the key.

`runAsUser` was only used to ensure the busybox command was executed by a non-root user.

This is likely leagues better than brute-forcing the file system permissions using an `initContainer`
> 1 votes

# Answer
You should try the access point in EFS, https://docs.aws.amazon.com/efs/latest/ug/efs-access-points.html

It enforces an operating system user and group, and a directory for every file system request made through the access point.

In other words, you can set the path as uid 1337 and gid 1337.
> 1 votes

---
Tags: kubernetes, kubernetes-helm
---


# Question
Title: How many executors can I have for a Jenkins Windows build node?
I have set up Windows build nodes in awhile, but I do remember when I did I was only able to set 1 executor per node. Is this still true?

# Answer
> 2 votes
You can specify the executors of your node in his configuration.

Manage Jenkins -\> Manage Nodes -\> Configure Node -\> Number of build processors

In environments where we only run scripts etc. we have one executor per core.

But if you have large build jobs and the performance is important for you, you should read this article from the jenkins documentation.

https://www.jenkins.io/doc/book/scaling/architecting-for-scale/#Calculating-how-many-jobs

# Answer
> 2 votes
The architecting for scale calcs don't give a good calculation, just a starting point. It does not address how often your jobs are triggered. The answer really depends on what you are executing and how often. Are you doing process intensive work, I/O heavy of network heavy work? Do you have a CI pipeline trigger on every commit (and how often is that), or manual jobs?

We have over 9000 jobs of all flavours, from manual and CI check-in triggered builds (Java and JS) and pipelines, to nightly integration/regression jobs, file management jobs, analysis jobs, some sometime once in a blue moon jobs, some interact w/real hardware or simulate waits or wait for remote results processing. About 1000 jobs execute/day. We have win nodes, linux nodes, common pool and label restricted nodes, all VM based ans dedicated to Jenkins. We even have some VMs running two different agent/nodes on same VM, different settings, effectively overallocating, but not in practice.

The short answer is: start with one executor/core and load it up, measure your loads (cpu/io/net/duration) and see. How long does each job take by itself (100%) vs one of many in parallel? Is the aggregate faster or longer? Gow do the loads compare?

We found when we know what we are running, at specific times we can easily go 2 executors/core. Every now and then, just 2 memory intensive jobs will exhaust memory, even when just 2/4 executiors in use (bad luck, restart one).

This may all be different if all your agents are cloud-based or ephemeral.

YMMV.

---
Tags: jenkins
---


# Question
Title: How to use script in a Gitlab bridge job?
I have a job which needs to include another job and needs to append a value to a variable, such as:

```
nightlies:
  variables:
    BUILD_NAME: "nightly"

  script:
    - $BUILD_NAME="${BUILD_NAME}-$(date +"%Y%m%d")"

  only:
    - schedules

  trigger:
    include:
      - local: /.gitlab-ci/build.yml
    strategy: depend

```

However reading the documentation `script` and `trigger` cannot be used together and I'm unsure of how else to create the `BUILD_NAME` with a date. Is there a way to use both. Otherwise, is there a way I could remove the trigger in order to use script, which would still call that job?

# Answer
One option is to inherit your environment variable from another job. You can create two different jobs, one to create the environment variable, and one to trigger the local CI pipeline.

Once all that is done, you should have something like this:

```
stages:
  - build
  - deploy

nightlies_env:
  stage: build
  variables:
    BUILD_NAME: "nightly"
  script:
    - BUILD_NAME="${BUILD_NAME}-$(date +"%Y%m%d")"
    - echo "BUILD_NAME=$BUILD_NAME" >> build.env
  artifacts:
    reports:
      dotenv: build.env

nightlies:
  stage: deploy
  variables:
    BUILD_NAME: $BUILD_NAME
  trigger:
    include: build.yml
    strategy: depend

```
> 1 votes

---
Tags: gitlab, builds
---


title: Log/Data rotation in Docker Container

text:
# Question
Title: Log/Data rotation in Docker Container
My question is more on the concept or best practices to follow.

I have two docker containers running InfluxDB and Grafana (monitoring applications). I have mounted external volume to each.

now, the problem is, these containers are running for quite some time (1 year approx), i am little bit worried about Host VM resources on which these containers are running. specifically storage resources as these are monitoring applications, which may be storing some data for historical purpose as well.

So, my ask is, what is the best practice to manage logs/data stored in docker volume. I don't want my Host VM to go out of storage and crash my containers.

Any possible solution, if we can rotate stored data so that my container won't stop and i have the historical data as well. or if possible from application point of view (InfluxDB and Grafana) to archive old data.

Thanks in advance... Please let me know if you need more details on this.

below is my docker-compose.yml file

```
version: "2"
services:
  grafana:
    image: grafana/grafana
    container_name: grafana
    user: root
    privileged: true
    restart: always
    ports:
      - 3000:3000
    networks:
      - monitoring
    volumes:
      - grafana-volume:/var/lib/grafana
  influxdb:
    image: influxdb
    container_name: influxdb
    restart: always
    ports:
      - 8086:8086
    networks:
      - monitoring
    volumes:
      - influxdb-volume:/var/lib/influxdb
networks:
  monitoring:
volumes:
  grafana-volume:
    external: true
  influxdb-volume:
    external: true

```

# Answer
You can copy the actual logs from `/var/lib/docker/containers/<container id>/<container id>-json.log` to archive them then clear the logs manually (since you have only 2 services, influxdb and grafana). The containers won't stop with this procedure.

There is a full and clear answer on this StackOverflow question about clearing container logs. It also explains how to configure the Docker daemon to rotate logs.

> From this question there's a one-liner that you can run:
>
> ```
> echo "" > $(docker inspect --format='{{.LogPath}}' <container_name_or_id>)
>
> ```
>
> \[...\]
>
> You can also set this as part of your daemon.json file instead of modifying your startup scripts:
>
> ```
> {
>   "log-driver": "json-file",
>   "log-opts": {"max-size": "10m", "max-file": "3"}
> }
>
> ```
>
> @BMitch - https://stackoverflow.com/a/42510314/3756843

As adviced, you should do this only until you configure the Docker daemon as your needs.
> 2 votes

# Answer
You can update the daemon.json config to specify your normal syslog driver (assuming linux) to use the normal syslog logrotation method for keeping log files or shipping off-box

https://docs.docker.com/config/containers/logging/syslog/

```
{
  "log-driver": "syslog",
  "log-opts": {
    "syslog-address": "udp://localhost:514"
  }
}

```
> 1 votes

---
Tags: docker, docker-compose, monitoring, databases, grafana
---


# Question
Title: Is there a way to export details of all EC2s in an AWS account?
I am aware there's a `Tag Editor` in AWS Console which allows us to export details of all EC2s in an AWS account.

However that list seems to be missing `ami and operating system` of the EC2.

Is there a way to export details including ami and operating system of each EC2 in an AWS account?

# Answer
> 5 votes
You can use AWS CLI and run the below command to get details of all ec2 instances in your account.

```
aws ec2 describe-instances

```

You can also use filters, if required. Please refer the documentation.

---
Tags: amazon-web-services, amazon-ec2
---