Running a bash script before startup in an NGINX docker container - javascript

I'm trying to run a javascript app on localhost:8000 using docker. Part of what I would like to do is swap out some config files based on the docker run command, I'd like to pass an environment variable into the container so that the bash script can use that as a parameter.
What my dockerfile is looking like is this:
FROM nginx
COPY . /usr/share/nginx/html
CMD ["bash","/usr/share/nginx/html/runfile.sh"]
And the bash script looks like this:
#!/bin/bash
if [ "$SECURITY_VERSION" = "OPENAM" ]; then
sed -i -e 's/localhost/openam/g' authConfig.js
fi
docker run -p 8000:80 missioncontrol:latest -e SECURITY_VERSION="TEST"
Docker gives me an exception saying -e exec command not found.
However if I change the dockerfile to use ENTRYPOINT instead of CMD, the -e flag works but the webserver does not start up.
Is there something I'm missing here? Is the ENTRYPOINT being overriden or something?
EDIT:
So I've updated my dockerfile to use ENTRYPOINT ["bash","/usr/share/nginx/html/runfile.sh", ";", " nginx -g daemon off;"]
But the docker container still shuts down. Is there something I'm missing?

NGINX 1.19 has a folder /docker-entrypoint.d on the root where place startup scripts executed by thedocker-entrypoint.sh script. You can also read the execution on the log.
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will
attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in
/docker-entrypoint.d/
/docker-entrypoint.sh: Launching
[..........]
/docker-entrypoint.sh: Configuration complete; ready for start up

For my future self and everybody else, this is how you can set up variable substitution at startup (for nginx, may also work for other images):
I've also wrote a more in depth blog post about it: https://danielhabenicht.github.io/docker/angular/2019/02/06/angular-nginx-runtime-variables.html
Dockerfile:
FROM nginx
ENV TEST="Hello variable"
WORKDIR /etc/nginx
COPY ./substituteEnv.sh ./substituteEnv.sh
# Execute the subsitution script and pass the path of the file to replace
ENTRYPOINT ["./substituteEnv.sh", "/usr/share/nginx/html/index.html"]
CMD ["nginx", "-g", "daemon off;"]
subsitute.sh: (same as #Daniel West's answer)
#!/bin/bash
if [[ -z $1 ]]; then
echo 'ERROR: No target file given.'
exit 1
fi
#Substitute all environment variables defined in the file given as argument
envsubst '\$TEST \$UPSTREAM_CONTAINER \$UPSTREAM_PORT' < $1 > $1
# Execute all other paramters
exec "${#:2}"
Now you can run docker run -e TEST="set at command line" -it <image_name>
The catch was the WORKDIR, without it the nginx command wouldn't be executed. If you want to apply this to other containers be sure to set the WORKDIR accordingly.
If you want to do the substitution recursivly in multiple files this is the bash script you are looking for:
# Substitutes all given environment variables
variables=( TEST )
if [[ -z $1 ]]; then
echo 'ERROR: No target file or directory given.'
exit 1
fi
for i in "${variables[#]}"
do
if [[ -z ${!i} ]]; then
echo 'ERROR: Variable "'$i'" not defined.'
exit 1
fi
echo $i ${!i} $1
# Variables to be replaced should have the format: ${TEST}
grep -rl $i $1 | xargs sed -i "s/\${$i}/${!i}/Ig"
done
exec "${#:2}"

I know this is late but I found this thread while searching for a solution so thought I'd share.
I had the same issue. Your ENTRYPOINT script should also include exec "$#"
#!/bin/sh
set -e
envsubst '\$CORS_HOST \$UPSTREAM_CONTAINER \$UPSTREAM_PORT' < /srv/api/default.conf > /etc/nginx/conf.d/default.conf
exec "$#"
That will mean the startup CMD from the nginx:alpine container will run. The above script will inject the specified environment variables into a config file. By doing this in runtime yo can override the environment variables.

Update the CMD line as below in the your dockerfile. Please note that if runfile.sh does not succeed (exit 0; inside it) then the next nginx command will not be executed.
FROM nginx
COPY . /usr/share/nginx/html
CMD /usr/share/nginx/html/runfile.sh && nginx -g 'daemon off;'
nginx docker file is using a CMD commnd to start the server on the base image you use. When you use the CMD command in your dockerfile you overwrite the one in their image. As it is mentioned in the dockerfile documentation:
There can only be one CMD instruction in a Dockerfile. If you list more than one CMD then only the last CMD will take effect.

NginX image has docker-entrypoint.d included and on container start will look for any scripts located in there. You can add your custom scripts during docker build. I also found that if you are using alpine image, bash is not installed, so you can add it yourself by running:
RUN apk update
RUN apk upgrade
RUN apk add bash
sample DockerFile:
FROM nginx:alpine
EXPOSE 443
EXPOSE 80
RUN apk update
RUN apk upgrade
RUN apk add bash
COPY ["my-script.sh", "/docker-entrypoint.d/my-script.sh"]
RUN chown nginx:nginx /docker-entrypoint.d/my-script.sh
USER nginx
In order to limit scope execution of your custom script script, it's highly recommended to run your container as a non-privileged user.

nginx container already defines ENTRYPOINT. If you define also CMD it will combine them both like 'ENTRYPOINT CMD' in such way that CMD becomes argument of ENTRYPOINT. That is why you need to redefine ENTRYPOINT to get it working.
Usually ENTRYPOINT is defined in such way, that if you also pass CMD, it will be executed by ENTRYPOINT script. However this might not be case with every container.

Related

start node and pm2 application from bat file

I have a chat-bot application running on node and I keep it always active thanks to pm 2.
I would like to improve the way I launch the application. Instead of running the start command from the console, it would be nice to double click a .bat file.
I am trying to develop the bat file, but I lack knowledge.
I am grateful for any help.
#echo off
SET PM2_HOME=C:\Users\Usuario\.pm2
pm2 start C:\Users\Usuario\Desktop\ROBOTs\Chatbot SCTR\app.js
echo servicio ejecutado
This bat file that I developed does not work. I know that I am not calling the variables, because I don't know how to include it, since I always execute the pm2 start app.js command.
my application does not use ports like 8080 and others, because the same library allows me to establish a connection and with pm 2 I keep it always active.
add the start command to your package.json for launching your app with pm2, then with your bat file just direct it to run with npm or yarn, whatever your default package manager is
edit:
here is a sample of a script in bash, but the concept will be the same for batch
#!/bin/bash
## detect operating system machine so we can setup some environment variables
UNAME="$(uname -s)"
case "${UNAME}" in
Linux*) OS='linux';;
Darwin*) OS='mac';;
CYGWIN*) OS='cygwin';;
MINGW*) OS='mingw';;
*) OS="UNKNOWN:${UNAME}"
esac
## if OS is Ubuntu (IE Production Box) set the path of variables
if [ $OS == 'linux' ]
then
YARN=/usr/bin/yarn
PM2=/usr/bin/pm2
fi
## if OS is Mac (IE Development Box) set the path of variables
if [ $OS == 'mac' ]
then
YARN=/usr/local/bin/yarn
PM2=/usr/local/bin/pm2
fi
## run the app
cd /var/www/application || exit
$YARN run productionStart
$PM2 save
exit $?
here is the line of code for starting the app on mac/linux we use from our package.json
"productionStart": "pm2 start ecosystem.config.js --env=production",
for more information about starting your app with an ecosystem file with pm2, see the docs here

how to get hot reload to work with React with Dotnet and Docker

I'm trying to get hot reload to work with React, Docker and Dotnet.
However, from what i found on the internet
only static rendering works with docker.
So i have to do
docker -t build {Name_of_file}
everytime to see changes within React....
I'm sure there's a way to do this,
here's my dockerfile.
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
RUN curl --silent --location https://deb.nodesource.com/setup_10.x | bash -
RUN apt-get install --yes nodejs
# Copy the source from your machine onto the container.
WORKDIR /src
COPY . .
RUN dotnet restore "./dotnet-test.csproj"
RUN dotnet publish "dotnet-test.csproj" -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:5.0
# Expose port 80 to your local machine so you can access the app.
EXPOSE 80
EXPOSE 443
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "dotnet-test.dll"]
If anyone has a github that does this let me know (:
Future me here!! Here is a link of me doing this with https
https://easyrun32.medium.com/net-5-react-docker-nginx-mysql-https-hotreload-50d87b32d492
Disclaimer: Not sure if this solution will work but give it a shot
Summary:
Use only the sdk image (runtime image won't work).
Use dotnet watch
Update .csproj file to include files to be watched
Long answer:
1. New Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
RUN curl --silent --location https://deb.nodesource.com/setup_10.x | bash -
RUN apt-get install --yes nodejs
# Copy the source from your machine onto the container.
WORKDIR /src
COPY . .
RUN dotnet restore "./dotnet-test.csproj"
RUN dotnet publish "dotnet-test.csproj" -c Release -o /app/publish
# Expose port 80 to your local machine so you can access the app.
EXPOSE 80
EXPOSE 443
WORKDIR /app/publish
ENTRYPOINT ["dotnet", "watch", "dotnet-test.dll"]
2. Project File Changes
<ItemGroup>
<!-- extends watching group to include *.js files -->
<Watch Include="**\*.js" Exclude="node_modules\**\*;**\*.js.map;obj\**\*;bin\**\*" />
</ItemGroup>
This should work for the most part. Make quick updates to include/exclude more files.

Docker: Why Do Variables Passed From .env Linger in the Container Even After They Are Removed From .env and Rebuilt

I'm trying to understand why env variables inside my Docker container keep appearing when I've clearly removed or commented them out from my .env file. I'm fairly new to Docker and don't know if this is expected behavior or an anomaly.
The way my system is setup, I spin up an instance of Azure's IoT Edge server locally (via deployment.template.json) which builds the Docker container and populates the environmental variables using the associated .env file.
Now what's perplexing me is that if I were to completely stop the server (not pause), comment out/remove the variable from the .env file, restart the server, and inspect the container (docker container inspect), I still see the variable name and value. I've also used docker system prune -a --volumes after stopping the server to prune my system and volumes, then restarted the server only to see the variable still listed.
Just in case it helps, inside my deployment.template.json I'm passing my variables as MY_VAR=${MY_VAR}. Then in my .env file I have the variable as MY_VAR=abc123.
From my Dockerfile:
# -------------
# Build Sources
# -------------
FROM node:10-alpine as builder
# Install additional git and openssh dependencies and make sure GitLab domain is accepted by SSH
RUN apk add --no-cache openssh git curl \
&& mkdir /root/.ssh/ \
&& touch /root/.ssh/known_hosts \
&& ssh-keyscan gitlab.com github.com >> /root/.ssh/known_hosts
WORKDIR /app
# Install app dependencies
RUN npm i -g typescript --no-cache --silent
COPY package*.json ./
RUN npm ci --only=production --silent
# Copy sources and build
COPY . .
RUN npm run build
# ----------------
# Production Image
# ----------------
FROM node:10-alpine
RUN apk add --no-cache curl
WORKDIR /app
COPY --from=builder /app/node_modules /app/node_modules
COPY --from=builder /app/dist /app/dist
COPY . .
USER node
CMD ["node", "dist/index.js"]
you can run "docker inspect" to your container and see what environment variables are defined in docker create options.
you can also check docker create options in Azure Portal.

How can I create a "tmp" directory with Elastic Beanstalk?

I'm using Node.js and need to save files to a tmp directory within my app. The problem is that Elastic Beanstalk does not set the app directory to be writable by the app. So when I try to create the temp directory I get this error
fs.js:653
return binding.mkdir(pathModule._makeLong(path),
^
Error: EACCES, permission denied '/var/app/tmp/'
at Object.fs.mkdirSync (fs.js:653:18)
at Promise.<anonymous> (/var/app/current/routes/auth.js:116:18)
at Promise.<anonymous> (/var/app/current/node_modules/mongoose/node_modules/mpromise/lib/promise.js:177:8)
at Promise.emit (events.js:95:17)
at Promise.emit (/var/app/current/node_modules/mongoose/node_modules/mpromise/lib/promise.js:84:38)
at Promise.fulfill (/var/app/current/node_modules/mongoose/node_modules/mpromise/lib/promise.js:97:20)
at /var/app/current/node_modules/mongoose/lib/query.js:1394:13
at model.Document.init (/var/app/current/node_modules/mongoose/lib/document.js:250:11)
at completeOne (/var/app/current/node_modules/mongoose/lib/query.js:1392:10)
at Object.cb (/var/app/current/node_modules/mongoose/lib/query.js:1151:11)
I've tried several things such as an app-setup.sh script within .ebextensions/scripts/app-setup.sh that looks like this
#!/bin/bash
# Check if this is the very first time that this script is running
if ([ ! -f /root/.not-a-new-instance.txt ]) then
newEC2Instance=true
fi
# Get the directory of 'this' script
dirCurScript=$(dirname "${BASH_SOURCE[0]}")
# Fix the line endings of all files
find $dirCurScript/../../ -type f | xargs dos2unix -q -k
# Get the app configuration environment variables
source $dirCurScript/../../copy-to-slash/root/.elastic-beanstalk-app
export ELASTICBEANSTALK_APP_DIR="/$ELASTICBEANSTALK_APP_NAME"
appName="$ELASTICBEANSTALK_APP_NAME"
dirApp="$ELASTICBEANSTALK_APP_DIR"
dirAppExt="$ELASTICBEANSTALK_APP_DIR/.ebextensions"
dirAppTmp="$ELASTICBEANSTALK_APP_DIR/tmp"
dirAppData="$dirAppExt/data"
dirAppScript="$dirAppExt/scripts"
# Create tmp directory
mkdir -p $dirApp/tmp
# Set permissions
chmod 777 $dirApp
chmod 777 $dirApp/tmp
# Ensuring all the required environment settings after all the above setup
if ([ -f ~/.bash_profile ]) then
source ~/.bash_profile
fi
# If new instance, now it is not new anymore
if ([ $newEC2Instance ]) then
echo -n "" > /root/.not-a-new-instance.txt
fi
# Print the finish time of this script
echo $(date)
# Always successful exit so that beanstalk does not stop creating the environment
exit 0
As well as creating a file called 02_env.config within .ebextensions that looks like this
# .ebextensions/99datadog.config
container_commands:
01mkdir:
command: "mkdir /var/app/tmp"
02chmod:
command: "chmod 777 /var/app/tmp"
Neither seem to work. How can I create a tmp directory within my app that is writable?
I recently experienced the same issue with a .NET application where the application was failing because it couldn't write to a directory, even after I had set the permissions.
What I found was that after the whole .ebextensions process was completed, the final step was a web container permissions update which ended up overwriting my ebextensions permissions change.
To solve it I moved the directory outside of the web container and updated the application to write there instead.
In your case I would suggest /tmp
With the newer (current?) Amazon Linux 2 elastic beanstalk installs, setting up a Post Deploy hook is the way to make this happen. The tmp folder needs to be created and made writeable AFTER elastic beanstalk has moved the newly deployed app bundle to /var/app. It's just a shell script placed in the following location from the root of your app:
.platform/hooks/postdeploy/10_create_tmp_and_make_writeable.sh
#!/bin/bash
mkdir /var/app/current/tmp
chmod 777 /var/app/current/tmp

Ubuntu 8.04 Hardy and node.js upstart script

I am trying to write an upstart script for my ubuntu machine, which is version 8.04 "Hardy". I have followed the instructions on this site: upstart for node.js but it seems like these instructions are for a current version of ubuntu.
I noticed that the /etc/init directory does not exist on my machine, first I tried putting the script in the /etc/init.d directory and then I created the /etc/init dir and placed it there.
I will post my upstart script below (which is basically the same as from the website above with some path changes), but when I run start jobname, I just get an error "start: Unknown job: jobname". So then I changed the script around to a slimmed down version, posted below, and still I get the same result.
For now, I am using the 'nohup' command to run my node server but I would like a more permanent solution.
Please, any help?
SCRIPT 1:
description "node.js chat server"
author "iandev ith3"
# used to be: start on startup
# until we found some mounts weren't ready yet while booting:
start on started mountall
stop on shutdown
# Automatically Respawn:
respawn
respawn limit 99 5
script
# Not sure why $HOME is needed, but we found that it is:
export HOME="/root"
exec /root/local/node/bin/node /home/ian/chat.js >> /var/log/node.log 2>&1
end script
post-start script
# optionally put a script here that will notifiy you node has (re)started
# /root/bin/hoptoad.sh "node.js has started!"
end script
SCRIPT 2:
description "node.js chat server"
author "iandev ith3"
script
exec /root/local/node/bin/node /home/ian/chat.js >> /var/log/node.log 2>&1
end script
Just use Forever. https://github.com/indexzero/forever
From looking at the website you provided I'd say that the /etc/init was just a typo and it should be /etc/init.d/. Some things you may want to check:
executable flag on your scripts. With most versions of Ubuntu executable files show up green when running 'ls' from the command line. If you want to check if your file is executable run 'ls -l /etc/init.d/YOUR_SCRIPT' from the command line. You will see something like this:
-rwxr-xr-x 1 root root 1342 2010-09-16 10:13 YOUR_SCRIPT
The x's mean that it is executable.
To set the executable flag if it is not set, run chmod u+x YOUR_SCRIPT
I'm pretty sure for older versions of ubuntu you need to have the script in /etc/rc.d/rc3.d or /etc/rc3.d. What linux does is run through rc0.d to rc5.d and execute every script in there. From what it looks like, ubuntu is moving away from this to something simpler so if you have rc directories you may need to edit your script a little.
Anyway I think i'm getting a little over complicated here. Check your executable flag and if you have rc directories and we'll move on from there.
May not be the best thing to start a process with sudo, but here's what I have setup on my local pc:
#!upstart
description "node.js server"
author "alessio"
start on startup
stop on shutdown
script
export HOME="/ubuntu"
exec sudo -u ubuntu /usr/bin/node /home/ubuntu/www/test.js 2>&1 >> /var/log/node.log
end script
Hope this helps.

Categories