How to Start ssh-agent in Dockerfile - bash

When entering my container, I want to log in as user ryan in directory /home/ryan/cas with the command eval "$(ssh-agent -c)" run. My following Dockerfile:
FROM ubuntu:latest
ENV TZ=Australia/Sydney
RUN set -ex; \
# NOTE(Ryan): Prevent docker build hanging on timezone confirmation
ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone; \
apt update; \
apt install -y --no-install-recommends \
sudo ca-certificates git gnupg openssh-client vim; \
useradd -m ryan -g sudo; \
printf "ryan ALL=(ALL:ALL) NOPASSWD:ALL" | sudo EDITOR="tee -a" visudo; \
# NOTE(Ryan): Prevent sudo usage prompt appearing on startup
touch /home/ryan/.sudo_as_admin_successful; \
git clone https://github.com/ryan-mcclue/cas.git /home/ryan/cas; \
chmod 777 -R /home/ryan/cas;
ENTRYPOINT ["/bin/bash", "-l", "-c"]
USER ryan
WORKDIR /home/ryan/cas
CMD eval "$(ssh-agent -s)"
However, running ssh-add I still get the Could not open a connection to your authentication agent which is indicative that the ssh-agent is not running. Manually typing eval "$(ssh-agent -c)" works.

I think you want remove your ENTRYPOINT statement, and then you want:
USER ryan
WORKDIR /home/ryan/cas
CMD ["ssh-agent", "bash", "-l"]
This will get you a login shell, run under the control of ssh-agent (so you'll have the necssary SSH_* environment variables and an active socket available).
To understand what's happening with your container, try running from the command line:
bash -l -c 'eval $(ssh-agent -s)'
What happens? The shell exits immediately, because running ssh-agent -s causes the agent to background itself, which looks pretty much the same as "exiting". Since you passed the -c flag, and the command given to -c has exited, the parent bash shell exits as well.

Related

How to catch SIGTERM properly in Docker?

I have a docker container created by the following Dockerfile:
ARG TAG=latest
FROM continuumio/miniconda3:${TAG}
ARG GROUP_ID=1000
ARG USER_ID=1000
ARG ORG=my-org
ARG USERNAME=user
ARG REPO=none
ARG COMMIT=none
ARG BRANCH=none
ARG MAKEAPI=True
RUN addgroup --gid $GROUP_ID $USERNAME
RUN adduser --uid $USER_ID --disabled-password --gecos "" $USERNAME --ingroup $USERNAME
COPY . /api_maker
RUN /opt/conda/bin/pip install pyyaml psutil packaging
RUN apt install -y openssh-client git
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
ENV GIT_SSH_COMMAND="ssh -i /run/secrets/thekey"
RUN --mount=type=secret,id=thekey git clone git#github.com:$ORG/$REPO.git /repo
RUN /opt/conda/bin/python3 /api_maker/repo_setup.py $BRANCH $COMMIT
RUN /repo/root_script.sh
RUN chown -R $USERNAME:$USERNAME /api_maker
RUN chown -R $USERNAME:$USERNAME /repo
RUN mkdir -p /data
RUN chown -R $USERNAME:$USERNAME /data
RUN mkdir -p /working
RUN chown -R $USERNAME:$USERNAME /working
RUN mkdir -p /opt/conda/pkgs
RUN mkdir -p /opt/conda/envs
RUN chmod -R 777 /opt/conda
RUN touch /opt/conda/pkgs/urls.txt
USER $USERNAME
RUN /api_maker/user_env_setup.sh $MAKEAPI
CMD /repo/run_api.sh $#;
with the following run_api.sh script:
#!/bin/bash
cd /repo
PROCESSES=${1:-9}
LOCAL_DOCKER_PORT=${2:-7001}
exec /opt/conda/envs/environment/bin/gunicorn --bind 0.0.0.0:$LOCAL_DOCKER_PORT --workers=$PROCESSES restful_api:app
My app contains some signal handling. If I manually send SIGTERM to gunicorn (either the worker or the parent process) from inside the container, my signal handling works properly. However, it does not work right when I run docker stop on the container. How can I make my shell script properly forward the SIGTERM it is supposedly receiving?
You need to make sure the main container process is your actual application, and not a shell wrapper.
As you have the CMD currently, a shell invokes it. The argument list $# will always be empty. The shell parses /repo/run_api.sh and sees that it's followed by a semicolon, so it might need to do something else. So even though your script correctly ends with exec gunicorn ... to hand off control directly to the other process, it's still running underneath a shell, and when you docker stop the container, it goes to the shell wrapper.
The easiest way to avoid this shell is to use an exec form CMD:
CMD ["/repo/run_api.sh"]
This will cause your script to run directly, without having a /bin/sh -c wrapper invoking it, and when the script eventually exec another process, that process becomes the main process and will receive the docker stop signal.

How to pass docker build command arguments to bash files

I am passing two command line arguments to my docker file like this:
docker build . -t ros-container --build-arg UBUNTU_VERSION=bionic --build-arg ROS_VERSION=melodic
I'm able to access them in my docker file, tho I couldn't get them in my bash files. I have tried both entrypoint and cmd techniques. But, non of them helped me.
Expectation
I want to access the two arguments,UBUNTU_VERSION & ROS_VERSION, from the 'script_init.bash' file. See the project structure.
Project structure
- ros_tutorials-noetic-devel
-Dockerfile
-scripts
-script_init.bash
Dockerfile
FROM ros:melodic-perception-bionic
# install packages
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -q && \
apt-get upgrade -yq && \
apt-get install -yq wget curl git build-essential vim sudo lsb-release locales bash-
completion
# Adjust working directory
RUN locale-gen en_US.UTF-8
RUN useradd -m -d /home/ubuntu ubuntu -p `perl -e 'print crypt("ubuntu",
"salt"),"\n"'` && \
echo "ubuntu ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# declare ros version arg
ARG ROS_VERSION
#declare ubuntu version arg
ARG UBUNTU_VERSION
# setup environment
USER ubuntu
WORKDIR /home/ubuntu
ENV UBUNTU_V=$UBUNTU_VERSION \
ROS_V=$ROS_VERSION
ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8
CMD COPY ./scripts/script_init.bash /
ENTRYPOINT ["/scripts/script_init.bash /"]
script_init.bash
#!/bin/bash
set -e
export UBUNTU_CODENAME=$UBUNTU_V
export REPO_DIR=$(dirname "$SCRIPT_DIR")
export CATKIN_DIR="$HOME/catkin_ws"
export ROS_DISTRO=$ROS_V
You need to copy the script file into your docker image and execute it correctly.
You should be able to get it working by using this Dockerfile, note the lines at the bottom:
FROM ros:melodic-perception-bionic
# install packages
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -q && \
apt-get upgrade -yq && \
apt-get install -yq \
bash-completion \
build-essential \
curl \
git \
locales \
lsb-release \
sudo \
vim \
wget
# Adjust working directory
RUN locale-gen en_US.UTF-8
RUN useradd -m -d /home/ubuntu ubuntu -p `perl -e 'print crypt("ubuntu", "salt"),"\n"'` && \
echo "ubuntu ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# declare ros version arg
ARG ROS_VERSION
#declare ubuntu version arg
ARG UBUNTU_VERSION
# setup environment
USER ubuntu
WORKDIR /home/ubuntu
ENV UBUNTU_V=$UBUNTU_VERSION \
ROS_V=$ROS_VERSION
ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8
# Copy your scripts directory into the docker image
COPY --chown=ubuntu:ubuntu scripts scripts
# Make sure you have execute permissions on the script
RUN chmod +x "./scripts/script_init.bash"
# Set your entrypoint to execute the script
ENTRYPOINT ["./scripts/script_init.bash"]
As a note, you could export all of these environment variables in the Dockerfile during the build without needing to execute a script at runtime, e.g. in your dockerfile:
# Export environment variables in Dockerfile
ENV UBUNTU_CODENAME=$UBUNTU_VERSION
ENV REPO_DIR=/home/ubuntu/scripts
ENV CATKIN_DIR=/home/ubuntu/catkin_ws
ENV ROS_DISTRO=$ROS_VERSION
I have finally found a solution that works like charm! Once you add the script folder, you can run it with bash command. In this way, you can pass what ever arguments to any bash file within the script folder.
# setup base image
FROM ros:melodic-perception-bionic
# install packages
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -q && \
apt-get upgrade -yq && \
apt-get install -yq wget curl git build-essential vim sudo lsb-release locales bash-completion
# Adjust working directory
RUN locale-gen en_US.UTF-8
RUN useradd -m -d /home/ubuntu ubuntu -p `perl -e 'print crypt("ubuntu", "salt"),"\n"'` && \
echo "ubuntu ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# declare ros version arg
ARG ROS_VERSION
#declare ubuntu version arg
ARG UBUNTU_VERSION
# setup environment
USER ubuntu
WORKDIR /home/ubuntu
ENV UBUNTU_V=$UBUNTU_VERSION \
ROS_V=$ROS_VERSION
ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8
# call script files
ADD /scripts /scripts
RUN bash /scripts/script_init.bash

Cant't build Jenkins latest within Docker

******** UPDATE *********
Bash script has no errors, checked with https://www.shellcheck.net/
Adding to the Dockerfilethe line
RUN tty | sed -e "s:/dev/::"
Outputs:
No tty
Next line on Dockerfile always fails:
ENTRYPOINT ["/usr/local/bin/jenkins.sh"]
I leave an image in order to clarify. In short, I think I need to attach a tty in some way to the batch script, but dunno how to do it.
Thanks
------------------- OLD CONTENT -------------------
I need to update a Jenkins image to 2.138.2. An excerpt of the original Dockerfile is as follows:
FROM openjdk:8-jdk
RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*
# ...
# Use tini as subreaper in Docker container to adopt zombie processes
COPY tini_pub.gpg ${JENKINS_HOME}/tini_pub.gpg
RUN curl -fsSL https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static-$(dpkg --print-architecture) -o /sbin/tini \
&& curl -fsSL https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static-$(dpkg --print-architecture).asc -o /sbin/tini.asc \
&& gpg --import ${JENKINS_HOME}/tini_pub.gpg \
&& gpg --verify /sbin/tini.asc \
&& rm -rf /sbin/tini.asc /root/.gnupg \
# ...
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/jenkins.sh"]
Using this Dockerfile FAILS due to gpg --import statement now needs to be fixed using --no-tty option. So that line remains as follows:
&& gpg --no-tty --import ${JENKINS_HOME}/tini_pub.gpg \
That's not fine since the execution of jenkins.sh now fails in several ways. The code of the script starts as follows:
#! /bin/bash -e
: "${JENKINS_WAR:="/usr/share/jenkins/jenkins.sh
This script is called from the Dockerfile in this line:
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/jenkins.sh"]
But now fails with several errors and seems to be impossible to process the file, nor removing the shebang line, nor removing the '-' or the '-e' option. The rest of the file is not processed fine if we change bash to other shell (not odd) nor removing the -e option (if I do that, the entrypoint does not find the jenkins.sh script).
Sumarizing, I've needed to remove a tty from gpg but doing that, I've lost access to bash scripting.
I've checked about the applied workaround, the workaround is described here ( (if I'm right, case is number 8, gpg might write to the tty at some point):
https://lists.gnupg.org/pipermail/gnupg-users/2017-April/058162.html
Is there any way to attach a tty to the entrypointor having any settings in the script in order to allow this work fine?
Thanks.
Finally runned on a Linux VM and no problems. Running it on Windows is the problem.

Failed to Call Access Method Exception when Creating a MedicationOrder in FHIR

I am using this http://fhirtest.uhn.ca/baseDstu2 test FHIR server and it worked okay so far.
Now I am getting an HTTP-500 - Failed to Call Access Method exception.
Anyone has any idea on what has gone wrong?
This happens frequently. Probably because someone tested weird queries or similar that put the server in an unstable status.
I suggest posting a comment in https://chat.fhir.org/#narrow/stream/hapi to get the server restarted,
or install http://hapifhir.io/doc_cli.html which does basically the same but you have full control.
I built a Dockerfile:
FROM debian:sid
MAINTAINER Günter Zöchbauer <guenter#yyy.com>
ENV DEBIAN_FRONTEND noninteractive
RUN \
apt-get -q update && \
DEBIAN_FRONTEND=noninteractive && \
apt-get install --no-install-recommends -y -q \
apt-transport-https \
apt-utils \
wget \
bzip2 \
default-jdk
# net-tools sudo procps telnet
RUN \
apt-get update && \
rm -rf /var/lib/apt/lists/*
https://github.com/jamesagnew/hapi-fhir/releases/download/v2.0/hapi-fhir-2.0-cli.tar.bz2 && \
ADD hapi-* /hapi_fhir_cli/
RUN ls -la
RUN ls -la /hapi_fhir_cli
ADD prepare_server.sh /hapi_fhir_cli/
RUN \
cd /hapi_fhir_cli && \
bash -c /hapi_fhir_cli/prepare_server.sh
ADD start.sh /hapi_fhir_cli/
WORKDIR /hapi_fhir_cli
EXPOSE 5555
ENTRYPOINT ["/hapi_fhir_cli/start.sh"]
Which requires in the same directory as the Dockerfile
prepare_server.sh
#!/usr/bin/env bash
ls -la
./hapi-fhir-cli run-server --allow-external-refs &
while ! timeout 1 bash -c "echo > /dev/tcp/localhost/8080"; do sleep 10; done
./hapi-fhir-cli upload-definitions -t http://localhost:8080/baseDstu2
./hapi-fhir-cli upload-examples -c -t http://localhost:8080/baseDstu2
start.sh
#!/usr/bin/env bash
cd /hapi_fhir_cli
./hapi-fhir-cli run-server --allow-external-refs -p 5555
Build
docker build myname/hapi_fhir_cli_dstu2 -t . #--no-cache
Run
docker run -d -p 5555:5555 [image id from docker build]
Hope this helps.

How can I set Bash aliases for docker containers in Dockerfile?

I am new to Docker. I found that we can set environment variables using the ENV instruction in the Dockerfile. But how does one set Bash aliases for long commands in Dockerfile?
Basically like you always do, by adding it to the user's .bashrc file:
FROM foo
RUN echo 'alias hi="echo hello"' >> ~/.bashrc
As usual this will only work for interactive shells:
docker build -t test .
docker run -it --rm --entrypoint /bin/bash test hi
/bin/bash: hi: No such file or directory
docker run -it --rm test bash
$ hi
hello
For non-interactive shells you should create a small script and put it in your path, i.e.:
RUN echo -e '#!/bin/bash\necho hello' > /usr/bin/hi && \
chmod +x /usr/bin/hi
If your alias uses parameters (ie. hi Jim -> hello Jim), just add "$#":
RUN echo -e '#!/bin/bash\necho hello "$#"' > /usr/bin/hi && \
chmod +x /usr/bin/hi
To create an alias of an existing command, might also use ln -s:
ln -s $(which <existing_command>) /usr/bin/<my_command>
If you want to use aliases just in Dockerfile, but not inside a container then the shortest way is the ENV declaration:
ENV update='apt-get update -qq'
ENV install='apt-get install -qq'
RUN $update && $install apt-utils \
curl \
gnupg \
python3.6
And for use in a container the way like already described:
RUN printf '#!/bin/bash \n $(which apt-get) install -qq $#' > /usr/bin/install
RUN chmod +x /usr/bin/install
Most of the time I use aliases just in the building stage and do not go inside containers, so the first example is quicker, clearer and simpler for every day use.
I just added this to my app.dockerfile file:
# Set up aliases
ADD ./bashrc_alias.sh /usr/sbin/bashrc_alias.sh
ADD ./initbash_profile.sh /usr/sbin/initbash_profile
RUN chmod +x /usr/sbin/initbash_profile
RUN /bin/bash -C "/usr/sbin/initbash_profile"
And inside the initbash_profile.sh file which just appends my custom aliases and no need to source the .bashrc file:
# Add the Bash aliases
cat /usr/sbin/bashrc_alias.sh >> ~/.bashrc
It worked a treat!
Another option is to just use the "docker exec -it <container-name> command" from outside the container and just use your own .bashrc or the .bash_profile file (what ever you prefer).
E.g.,
docker exec -it docker_app_1 bash
I think the easiest way would be to mount a file into your container containing your aliases, and then specify where Bash should find it:
docker run \
-it \
--rm \
-v ~/.bash_aliases:/tmp/.bash_aliases \
[image] \
/bin/bash --init-file /tmp/.bash_aliases
Sample usage:
echo 'alias what="echo it works"' > my_aliases
docker run -it --rm -v ~/my_aliases:/tmp/my_aliases ubuntu:18.04 /bin/bash --init-file /tmp/my_aliases
alias
Output:
alias what='echo it works'
what
Output:
it works
You can use ENTRYPOINT, but it will not work for aliases, in your Dockerfile:
ADD dev/entrypoint.sh /opt/entrypoint.sh
ENTRYPOINT ["/opt/entrypoint.sh"]
Your entrypoint.sh file:
#!/bin/bash
set -e
function dev_run()
{
}
export -f dev_run
exec "$#"
Here is a Bash function to have your aliases in every container you use interactively.
ducker_it() {
docker cp ~/bin/alias.sh "$1":/tmp
docker exec -it "$1" /bin/bash -c "[[ ! -f /tmp/alias.sh.done ]] \
&& [[ -w /root/.bashrc ]] \
&& cat /tmp/alias.sh >> /root/.bashrc \
&& touch /tmp/alias.sh.done"
docker exec -it "$1" /bin/bash
}
Required step before:
grep ^alias ~/.zshrc > ~/bin/alias.sh
Used some of the previous solutions, but the aliases are not recognised still.
I'm trying to set aliases and use them both within later Dockerfile steps and in the container at runtime.
RUN echo "alias model-downloader='python3 ${MODEL_DL_PATH}/downloader.py'" >> ~/.bash_aliases && \
echo "alias model-converter='python3 ${MODEL_DL_PATH}/converter.py'" >> ~/.bash_aliases && \
source ~/.bash_aliases
# Download the model
RUN model-downloader --name $MODEL_NAME -o $MODEL_DIR --precisions $MODEL_PRECISION;
The solution for me was to use ENV variables that held folder paths and then add the exact executable. I could have use ARG too, but for more of my scenarios I needed the aliases in both the build stage and later in the runtime.
I used the ENV variables in conjunction with a Bash script that runs once dependencies have ponged and sets the Bash source, sets some more env variables, and allows for further commands to pipe through.
#ErikDannenberg's answer did the trick, but in my case, some adjustments were needed.
It didn't work with aliases cause apparently there's an issue with interactive shells.
I reached for his second solution, but it still didn't really work. I checked existing shell scripts in my project and noticed the head comment (first line = #!/usr/bin/env sh) differs a bit from #!/usr/bin/bash. After changing it accordingly it started working for my t and tc "aliases", but I had to use the addendum to his second solution for getting tf to work.
Here's the complete Dockerfile
FROM php:8.1.1-fpm-alpine AS build
RUN apk update && apk add git
RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
RUN apk add --no-cache $PHPIZE_DEPS \
&& pecl install xdebug \
&& docker-php-ext-enable xdebug \
&& touch /usr/local/etc/php/conf.d/99-xdebug.ini \
&& echo "xdebug.mode=coverage" >> /usr/local/etc/php/conf.d/99-xdebug.ini \
&& echo -e '#!/usr/bin/env sh\nphp artisan test' > /usr/bin/t \
&& chmod +x /usr/bin/t \
&& echo -e '#!/usr/bin/env sh\nphp artisan test --coverage' > /usr/bin/tc \
&& chmod +x /usr/bin/tc \
&& echo -e '#!/usr/bin/env sh\nphp artisan test --filter "$#"' > /usr/bin/tf \
&& chmod +x /usr/bin/tf
WORKDIR /var/www

Resources