ECS Task Definition - When overriding ENTRYPOINT, Docker image's CMD is dropped - amazon-ecs

I have a Docker Image built with the following CMD
# Dockerfile
...
CMD ["nginx", "-g", "daemon off;"]
When my task definition does not include entryPoint or command the task successfully enters a running state.
{
"containerDefinitions": [
{
"image": "<myregistry>/<image>",
...
}
]
}
I need to run an agent in some instances of this container, so I am using an entrypoint for this task to run my agent. The problem is when I add an entryPoint parameter to the task definition, the container starts and immediately stops.
This is what I'm doing to add the entryPoint:
{
"containerDefinitions": [
{
"image": "<myregistry>/<image>",
...
"entryPoint": [
"custom-entry-point.sh"
],
}
]
}
And here is the contents of custom-entry-point.sh:
#!/bin/bash
/myagent &
echo "CMD is: $#"
exec "$#"
To confirm my suspicion that CMD is dropped, the logs just show:
CMD is:
If I add the CMD array from the Dockerfile to the task definition with the command parameter, it works fine and the task starts:
{
"containerDefinitions": [
{
"image": "<myregistry>/<image>",
...
"entryPoint": [
"custom-entry-point.sh"
],
"command": [
"nginx",
"-g",
"daemon off;"
}
]
}
And the logs show the expected:
CMD is: nginx -g daemon off;
I have many docker images with various iterations of CMD, I do not want to have to copy these into my task definitions. It seems that just adding only an entryPoint to a task definition should not override a docker image's CMD with an empty value.
Hoping some ECS / fargate experts can help shed some light on a path forward.

Some tips:
Check if your entrypoint script is executable
Use absolute path to your entrypoint script
Check logs to see the error. hopefully you autoconfigured awslog driver?
Have you successfully run the entrypoint version on your local?
Also have a read of this for some useful background:
https://aws.amazon.com/blogs/opensource/demystifying-entrypoint-cmd-docker/

I don't think this has anything to do with ECS. This is how Docker behaves, and there's no way to change it as far as I know.
See https://docs.docker.com/engine/reference/builder/
If CMD is defined from the base image, setting ENTRYPOINT will reset CMD to an empty value. In this scenario, CMD must be defined in the current image to have a value.
This particular snippet only refers to defining a new ENTRYPOINT in the image, but this Github discussion confirms the same behavior holds when overriding ENTRYPOINT at runtime.

I got the same problem, with my entrypoint and command attributes being something like sh -c .... I needed to delete sh -c, put the commands directly, and add #!/bin/sh at the top of my scripts.

Related

VSCode-Docker Not Invoking "CMD"

TL;DR Image built by VSCode only executes the CMD command when I press the Run button in the Docker Desktop UI.
Hello Folks,
I'm playing around with a Drools image along with Docker Desktop and VSCode.
My devcontainer.json file looks like the following:
{
"name": "Existing Dockerfile",
"build": {
// Sets the run context to one level up instead of the .devcontainer folder.
"context": "..",
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
"dockerfile": "../Dockerfile"
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [8001,8080]
}
My Dockerfile is minimalist and looks like the following:
FROM quay.io/kiegroup/business-central-workbench:latest
And my compose.yaml file looks like so:
services:
app:
entrypoint:
- sleep
- infinity
image: docker/dev-environments-default:stable-1
init: true
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
The issue is that when VSCode sends the image to Docker Desktop, the
CMD ["./start_business-central-wb.sh"] found in the parent image does not seem to be getting set off as seen in the logs
However, when I click "Run" the command gets kicked off after spawning a new instance
What concept am I missing as to why the Docker image doesn't immediately begin running when VSCode sends it to Docker Desktop? I'm super inexperienced with both techs.
Any help is greatly appreciated.

Running nx target only if file doesn't exist

I have a project which has a build step, however, I need to make sure that the file firebase.config.json exists before running the build command.
With that, I have two NPM scripts:
// package.json
{
...,
"nx": {
"targets": {
"prepare": {
"outputs": ["firebase.config.json"]
},
"build": {
"outputs": ["dist"],
"dependsOn": [
{
"target": "prepare",
"projects": "self"
}
]
}
}
},
"scripts": {
"prepare": "firebase apps:sdkconfig web $FIREBASE_APP_ID_SHOP --json | jq .result.sdkConfig > firebase.config.json",
"build": "VITE_FIREBASE_CONFIG=$(cat ./firebase.config.json) vite build",
},
...
}
So with the above, every time I run nx build app it will first run prepare and build the firebase.config.json file.
However, every time I make a change to any of the source files inside my project, prepare re-runs even though the firebase.config.json is already present.
Is it possible for nx to only run a target if the file declared under outputs is not present?
If you are in a bash environment you can modify your prepare script to be the following (note the original command has been shortened with ellipses for readability).
// package.json
{
"scripts":{
"prepare": "CONFIG=firebase.config.json; [ -f \"$CONFIG\" ] || firebase apps:sdkconfig ... | jq ... > \"$CONFIG\""
}
}
The above prepare script will still run, but it should not spend any time reproducing the configuration file if it already exists.
CONFIG=firebase.config.json is just putting our file in a bash environment variable so we can use it in multiple places (helps prevent typos). [ -f "$CONFIG" ] will return true if $CONFIG holds a filename which corresponds to an existing file. If it returns true, it will short-circuit the || (OR) command.
If you want further verification of this technique, you can test this concept at the terminal with the command [ -f somefile.txt ] || echo "File does not exist". If somefile.txt does not exist, then the echo will run. If the file does exist, then the echo will not run.
A slightly-related side-note: while you clearly can do this all in the package.json configuration, if your nx workspace is going to grow to include other libraries or applications, I highly recommend splitting up all your workspace configuration into the default nx configuration files: nx.json, workspace.json, and the per-project project.json files for the sake of readability/maintainability.
Best of luck!

Using kfp.dls.containerOp() to run multiple scripts on Kubeflow Pipelines

I have been using the Kubeflow dsl container op command to run a python script on a custom for my Kubeflow pipeline. My configuration looks something like this :
def test_container_op():
input_path = '/home/jovyan/'
return dsl.ContainerOp(
name='test container',
image="<image name>",
command=[
'python', '/home/jovyan/test.py'
],
file_outputs={
'modeule-logs' : input_path + 'output.log'
}
)
Now, I also want to run a bash script called deploy.sh within the same container. I haven't seen examples of that. Is there something like
command = [
'/bin/bash', '/home/jovyan/deploy.sh',
'python', '/home/jovyan/test.py'
]
Not sure if it's possible. Would appreciate the help.
Kubeflow job is just a Kubernetes job, thus you are limited with Kubernetes job entrypoint being a single command.
However you can still chain multiple commands into a single sh command:
sh -c "echo 'my first job' && echo 'my second job'"
So that you kubeflow command can be:
command = [
'/bin/sh', '-c', '/home/jovyan/deploy.sh && python /home/jovyan/test.py'
]

How to run a python function or script somescript.py on the KubernetesPodOperator in airflow?

I am running a Celery Executor and I'm trying to run some python script in the KubernetesPodOperator. Below are examples of what I have tried that didn't work. What am I doing wrong?
Running sctipt
org_node = KubernetesPodOperator(
namespace='default',
image="python",
cmds=["python", "somescript.py" "-c"],
arguments=["print('HELLO')"],
labels={"foo": "bar"},
image_pull_policy="Always",
name=task,
task_id=task,
is_delete_operator_pod=False,
get_logs=True,
dag=dag
)
Running function load_users_into_table()
def load_users_into_table(postgres_hook, schema, path):
gdf = read_csv(path)
gdf.to_sql('users', con=postgres_hook.get_sqlalchemy_engine(), schema=schema)
org_node = KubernetesPodOperator(
namespace='default',
image="python",
cmds=["python", "somescript.py" "-c"],
arguments=[load_users_into_table],
labels={"foo": "bar"},
image_pull_policy="Always",
name=task,
task_id=task,
is_delete_operator_pod=False,
get_logs=True,
dag=dag
)
The script somescript.py must be in Docker image.
Step-1: let's create a image https://docs.docker.com/develop/develop-images/dockerfile_best-practices/.
FROM python:3.8
# copy requirement.txt from local to container
COPY requirements.txt requirements.txt
# install dependencies into container (geopandas, sqlalchemy)
RUN pip install -r requirements.txt
# copy the python script from local to container
COPY somescript.py somescript.py
ENTRYPOINT [ "python", "somescript.py"]
Step-2: Build and push the image into public Docker repository https://hub.docker.com.
NB: kubernetes_pod_operator looks for image from public docker repo
# build image
docker build -t my-python-img:latest .
# test if your image works perfectly
docker run my-python-img:latest
# push image.
docker tag my-python-img username/my-python-img
docker push username/my-python-img
docker pull username/my-python-img
step-3: Lest's create k8s task.
p = KubernetesPodOperator(
namespace='default',
image='username/my-python-img:latest',
labels={'dag-id': dag.dag_id},
name='airflow-my-image-pod',
task_id='load-users',
in_cluster=False, #False: local, True: cluster
cluster_context='microk8s',
config_file='/usr/local/airflow/include/.kube/config',
is_delete_operator_pod=True,
get_logs=True,
dag=dag
)
If you don't understand where configuration file comes from, look here: https://www.astronomer.io/docs/cloud/stable/develop/kubepodoperator-local.
Finally: I want to mention something important when working with databases (credentials). Kubernetes offers the use secret to secure sensitive information. https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/stable/operators.html
KubernetesPodOperator launches a Kubernetes pod that runs a container as specified in the operator's arguments.
First Example
In the first example, the following happens:
KubernetesPodOperator instructs K8s to lunch a pod and prepare to run a container in it using the python image (the image parameter) from hub.docker.com (the default image registry)
ENTRYPOINT of the python image is replaced by ["python", "somescript.py" "-c"] (the cmd parameter)
CMD of the python image is replaced by ["print('HELLO')"] (the arguments parameter)
...
The container is run
So, the complete command that is run in the container is
python somescript.py -c print('HELLO')
Obviously, the official Python image from Docker Hub does not have somescript.py in its working directory. Even if did, it probably would have been not the one that you wrote. That is why the command fails with something like:
python: can't open file 'somescrit.py': [Errno 2] No such file or directory
Second Example
In the second example, pretty much the same happens as in the first example, but the command that is run in the container (again based on the cmd and arguments parameters) is
python somescript.py -c None
(None is the string representation of the load_users_into_table()'s return value)
This command fails, because of the same reasons as in the first example.
How It Could be Done (a Sketch)
You could build a Docker image with somescript.py and all its dependencies. Push the image to an image registry. Specify the image, ENTRYPOINT, and CMD in the corresponding parameters of KubernetesPodOperator.

problem loading in ${localEnv:TOKEN} into devcontainer.json

In my devcontainer.json for vscode, I am trying to load in a build variable. This variable is on my local machine's environment, my code looks like the following:
//build arguments
"build": {
"args": {
"TOKEN": "${localEnv:TOKEN}"
}
}
It seems like it works when I put in a direct string, or something like "${localEnv:HOME}", but it is not picking up this custom one. which is strange because I can do 'printenv TOKEN' and it prints out correctly.
any ideas on what I may be doing wrong?
Add your export BLA=1 to .profile, this was the only way VScode was able to pass through env variables to the devcontainer.
.devcontainer:
{
"name": "devcontainer",
"build": {
"dockerfile": "${localWorkspaceFolder}/Dockerfile",
"context": "${localWorkspaceFolder}",
},
"remoteEnv": {
"FOO": "${localEnv:FOO}",
"BAR": "${localEnv:BAR}",
}
}
First, ensure that you have the VS Code Terminal -> Integrated: Inherit Env setting set to true. This is described on the Advanced Container Configuration page:
Workarounds
If that doesn't fix your problem (it didn't for me), here are some of the workarounds that I have found:
Set the variables in your ~/.bashrc file (or export them temporarily in the terminal) and start VS Code from a bash prompt (the executable is code).
$ export TOKEN=tokenvalue
$ code
Set the variables in your ~/.pam_environment file (these are available session wide and are inherited by applications started with the launcher). You will need to logout and login or reboot for these to apply.
TOKEN=tokenvalue
Set the environment variables in one of your VS Code settings files (user or workspace) using the Terminal -> Integrated Env: Linux setting:
// Object with environment variables that will be added to the VS Code process to be used by the terminal on Linux. Set to `null` to delete the environment variable.
"terminal.integrated.env.linux": {
"TOKEN": "tokenvalue"
},