Docker-image-as-executable: should I execute in CMD or ENTRYPOINT? - docker

I am creating a Docker image to initialize my PostgreSQL database. It looks like this:
FROM debian:stretc
RUN set -x \
&& ... ommitted ...
&& apt-get install postgresql-client -y
COPY scripts /scripts
CMD cd /scripts && psql -f myscript.sql
This works great. Every time I need to initialize my database I start the container (docker run --rm my-image). After the psql command is done, the container is automatically stopped and removed (because of the --rm). So basically, I have a Docker-image-as-executable.
But, I am confused whether that last line should be:
CMD cd /scripts && psql -f myscript.sql
or
ENTRYPOINT cd /scripts && psql -f myscript.sql
Which one should be used in my case (Docker-image-as-excutable)? Why?

You need to use ENTRYPOINT if you need to make it as "Docker-image-as-executable"
RUN executes the command(s) that you give in a new layer and creates
a new image. This is mainly used for installing a new package.
CMD sets default command and/or parameters, however, we can overwrite
those commands or pass in and bypass the default parameters from
the command line when docker runs
ENTRYPOINT is used when yo want to run a container as an executable.

Both Entrypoint and Command will do the same thing. The only main difference is that when you use CMD you have more flexibilty in overriding the command that runs from the CLI.
so If you have the dockerfile:
FROM debian:stretc
RUN set -x \
&& ... ommitted ...
&& apt-get install postgresql-client -y
COPY scripts /scripts
CMD cd /scripts && psql -f myscript.sql
You can override the CMD defined in the dockerfile from the cli to run a different command:
docker run --rm my-image psql -f MYSCRIPT2.sql
This will run MYSCRIPT2.sql as given in the cli. You can't do that with ENRTYPOINT.

Related

My custom beat cant find custombeat.yml when I try to run it from a container

So, I have built a beat with mage GenerateCustomBeat and it runs okay, except, now I'm trying to cotainerize it. When I run the image I built, it complains that no customBeat.yml was found.
I have secured that the file exists in the folder by adding a line RUN ls . at the end of my Dockerfile.
The beat name is coletorbeat, so this name appears multiple times inside the Dockerfile.
Upon executing sudo docker run coletorbeat I have the following error message:
Exiting: error loading config file: stat coletorbeat.yml: no such file or directory
If there was a way to specify the coletorbeat.yml file location when I execute the beat, in CMD I think I would solve it, but I have not found how to do so yet.
I'll post the Dockerfile below. I know the code inside the beater folder works fine. I'm guessing I'm making some mistake on the containerization.
Dockerfile:
FROM ubuntu
MAINTAINER myNameHere
ARG ${ip:-"333.333.333.333"}
ARG ${porta:-"4343"}
ARG ${dataInicio:-"2020-01-07"}
ARG ${dataFim:-"2020-01-07"}
ARG ${tipoEquipamento:-"type"}
ARG ${versao:-"2"}
ARG ${nivel:-"0"}
ARG ${instituicao:-"RJ"}
ADD . .
RUN mkdir /etc/coletorbeat
COPY /coletorbeat/coletorbeat.yml /etc/coletorbeat/coletorbeat.yml
RUN apt-get update && \
apt-get install -y wget git
RUN wget https://storage.googleapis.com/golang/go1.14.4.linux-amd64.tar.gz
RUN tar -zxvf go1.14.*.linux-amd64.tar.gz -C /usr/local
RUN mkdir /go
ENV GOROOT /usr/local/go
ENV GOPATH $HOME/go
ENV PATH $PATH:$GOROOT/bin:$GOPATH/bin
RUN echo $PATH
RUN go get -u -d github.com/magefile/mage
RUN cd $GOPATH/src/github.com/magefile/mage && \
go run bootstrap.go
RUN apt-get install -y python3-venv
RUN apt-get install -y build-essential
RUN cd /coletorbeat && chmod go-w coletorbeat.yml && ./coletorbeat setup
RUN cd /coletorbeat && ./coletorbeat test config -c /coletorbeat/coletorbeat.yml && ls .
CMD ./coletorbeat/coletorbeat -E 'coletorbeat.ip=${ip}'
You are adding the yml file into the /etc dir
COPY /coletorbeat/coletorbeat.yml /etc/coletorbeat/coletorbeat.yml
But then running commands on /coletorbeat without using etc.
On CMD line in the Dockerfile, I added the command cd /mybeatfolder and it worked. Libbeat searches the current folder for the config file as default, so moving to the right directory before executing my beat solved it.

CentOS7: How to start the slapd service in a docker container?

I want to run an OpenLDAP server in a docker container using CentOS7.
I managed to have a container running with an openldap installed in it. My problem is that I am using a script entrypoint.sh to start the slapd service and add a user to my directory. I would like this two steps to be in the Dockerfile so that the password to perform ldapadd is not stored in the script.
So far I have only found examples on debian .
https://github.com/kanboard/docker-openldap/blob/master/memberUid/Dockerfile this is what I would like to do but using CentOS 7.
I tried start slapd service in my Dockerfile without success.
My Dockerfile looks like this :
FROM centos:7
RUN yum -y update && yum -y install \
openldap-servers \
openldap-clients \
libselinux-python \
openssl \
; yum clean all
RUN chown ldap:ldap -R /var/lib/ldap
COPY slapd.conf /etc/openldap/slapd.conf
COPY base.ldif /etc/openldap/schema/base.ldif
COPY entrypoint.sh /entrypoint.sh
RUN chmod 500 /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
My entrypoint.sh script looks like this :
#!/bin/bash
exec /usr/sbin/slapd -f /etc/openldap/slapd.conf -h "ldapi:/// ldap:///" -d stats &
sleep 10
ldapadd -x -w mypassword -D "cn=ldapadm,dc=mydomain" -f /etc/openldap/schema/base.ldif
This does work however I am looking to start the ldap service and do the ldapadd command in the Dockerfile not to have mypassword stored in entrypoint.sh.
Hence I tried these commands :
RUN systemctl slapd start
RUN ldapadd -x -w password -D "cn=ldapadm,dc=mydomain" -f /etc/openldap/schema/base.ldif
Of course this does not work as systemctl does not work in Dockerfile. What is the best alternative ? I was considering having one container starting the ldap servcie but then I do not know how to call it to build the image of the other container...
EDIT :
Thanks to Guido U. Draheim, I have an alternative to systemctl to start slapd service.
My Dockerfile now looks like this :
FROM centos:7
RUN yum -y update && yum -y install \
openldap-servers \
openldap-clients \
libselinux-python \
openssl \
; yum clean all
RUN chown ldap:ldap -R /var/lib/ldap
COPY slapd.conf /etc/openldap/slapd.conf
COPY base.ldif /etc/openldap/schema/base.ldif
COPY files/docker/systemctl.py /usr/bin/systemctl
RUN systemctl enable slapd
RUN systemctl start slapd;\
ldapdd -x -w password -D "cn=ldapadm,dc=sblanche" -f /etc/openldap/schema/base.ldif
COPY entrypoint.sh /entrypoint.sh
RUN chmod 500 /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
But I have got the following error : ldap_bind: Invalid credentials (49)
(a) you could use the docker-systemctl-replacement to run your "systemctl.py start slapd". Which is the obvious first error.
(b) each RUN in a dockerfile is a new container, so the running process from the earlier invocation can not survive anyway. That's why the referenced dockerfile example has it combined with "&&".
And yeah (c) I am using an openldap centos container. So go ahead, try again.

docker run fails with /bin/sh:0 -c requires an argument

I am trying to run a docker image
Dockerfile
FROM marketplace.gcr.io/google/ubuntu1804:latest
MAINTAINER Vinay Joseph (vinay.joseph#microfocus.com)
LABEL ACI_COMPONENT="License Server"
EXPOSE 20000/tcp
#Install Unzip
RUN apt-get install unzip
#Unzip License Server to /opt/MicroFocus
RUN mkdir /opt/MicroFocus
RUN cd /opt/MicroFocus
#Download the License Server
RUN curl -O https://storage.googleapis.com/software-idol-21/LicenseServer_12.1.0_LINUX_X86_64.zip
RUN chmod 777 LicenseServer_12.1.0_LINUX_X86_64.zip
RUN unzip LicenseServer_12.1.0_LINUX_X86_64.zip
cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/xxxx/idol-licenseserver', '.']
images:
- 'gcr.io/xxxx/idol-licenseserver'
The message i get is
docker run gcr.io/xxxx/idol-licenseserver
/bin/sh: 0: -c requires an argument
There are a couple of problems with your Dockerfile
First
RUN apt-get install unzip
A good practice is to perform an update before installing packages, otherwise you could fall into situation with missing package lists.
RUN apt-get update && apt-get install -y ...
Second
RUN mkdir /opt/MicroFocus
RUN cd /opt/MicroFocus
This is mistake because cd doesn't work between layers (different RUN commands). What you wanted is achieved with single WORKDIR command
WORKDIR /opt/MicroFocus
Third
The error message that you are facing means that base image is configured with something like ENTRYPOINT ["sh", "-c"] therefore expecting you to provide initial command line when launching this image. You have to define the proper startup command and append it to your command after image name.
ENTRYPOINT ["/bin/sh", "-c"] is the default entrypoint in every Dockerfile if you do not choose your own entrypoint. If you run the Dockerfile, add a command of your choice that you would like to run. At best just try bash:
docker run -it gcr.io/xxxx/idol-licenseserver bash
Without adding any command, the container does not know what to run in the command line but still starts the bash (sh in this case) to run something, waiting for a command = -c requires an argument.

Modifying a Docker container's ENTRYPOINT to run a script before the CMD

I'd like to run a script to attach a network drive every time I create a container in Docker. From what I've read this should be possible by setting a custom entrypoint. Here's what I have so far:
FROM ubuntu
COPY *.py /opt/package/my_code
RUN mkdir /efs && \
apt-get install nfs-common -y && \
echo "#!/bin/sh" > /root/startup.sh && \
echo "mount -t nfs4 -o net.nfs.com:/ /nfs" >> /root/startup.sh && \
echo "/bin/sh -c '$1'" >> /root/startup.sh && \
chmod +x /root/startup.sh
WORKDIR /opt/package
ENV PYTHONPATH /opt/package
ENTRYPOINT ["/root/startup.sh"]
At the moment my CMD is not getting passed through properly to my /bin/sh line, but I'm wondering if there isn't an easier way to accomplish this?
Unfortunately I don't have control over how my containers will be created. This means I can't simply prepend the network mounting command to the original docker command.
From documentation:
CMD should be used as a way of defining default arguments for an ENTRYPOINT command or for executing an ad-hoc command in a container
So if you have an ENTRYPOINT specified, the CMD will be passed as additional arguments for it. It means that your entrypoint script should explicitly handle these arguments.
In your case, when you run :
docker run yourimage yourcommand
What is executed in your container is :
/root/startup.sh yourcommand
The solution is to add exec "$#" at the end of your /root/startup.sh script. This way, it will execute any command given as its arguments.
You might want to read about the ENTRYPOINT mechanisms and its interaction with CMD.

Change directory command in Docker?

In docker I want to do this:
git clone XYZ
cd XYZ
make XYZ
However because there is no cd command, I have to pass in the full path everytime (make XYZ /fullpath). Any good solutions for this?
To change into another directory use WORKDIR. All the RUN, CMD and ENTRYPOINT commands after WORKDIR will be executed from that directory.
RUN git clone XYZ
WORKDIR "/XYZ"
RUN make
You can run a script, or a more complex parameter to the RUN. Here is an example from a Dockerfile I've downloaded to look at previously:
RUN cd /opt && unzip treeio.zip && mv treeio-master treeio && \
rm -f treeio.zip && cd treeio && pip install -r requirements.pip
Because of the use of '&&', it will only get to the final 'pip install' command if all the previous commands have succeeded.
In fact, since every RUN creates a new commit & (currently) an AUFS layer, if you have too many commands in the Dockerfile, you will use up the limits, so merging the RUNs (when the file is stable) can be a very useful thing to do.
I was wondering if two times WORKDIR will work or not, but it worked :)
FROM ubuntu:18.04
RUN apt-get update && \
apt-get install -y python3.6
WORKDIR /usr/src
COPY ./ ./
WORKDIR /usr/src/src
CMD ["python3", "app.py"]
You can use single RUN command for all of them
RUN git clone XYZ && \
cd XYZ && \
make XYZ
In case you want to change the working directory for the container when you run a docker image, you can use the -w (short for --workdir) option:
docker run -it -w /some/valid/directory/inside/docker {image-name}
Ref:
docker run options: https://docs.docker.com/engine/reference/commandline/run/#options
Mind that if you must run in bash shell, you need not just the RUN make, but you need to call the bash shell before, since in Docker, you are in the sh shell by default.
Taken from /bin/sh: 1: gvm: not found, which would say more or less:
Your shell is /bin/sh, but source expects /bin/bash, perhaps because it
puts its initialization in ~/.bashrc.
In other words, this problem can occur in any setting where the "sh" shell is used instead of the "bash", causing "/bin/sh: 1: MY_COMMAND: not found".
In the Dockerfile case, use the recommended
RUN /bin/bash -c 'source /opt/ros/melodic/setup.bash'
or with the "[]" (which I would rather not use):
RUN ["/bin/bash", "-c", "source /opt/ros/melodic/setup.bash"]
Every new RUN of a bash is isolated, "starting at 0". For example, mind that setting WORKDIR /MY_PROJECT before the bash commands in the Dockerfile does not affect the bash commands since the starting folder would have to be set in the ".bashrc" again. It needs cd /MY_PROJECT even if you have set WORKDIR.
Side-note: do not forget the first "/" before "opt/../...". Else, it will throw the error:
/bin/bash: opt/ros/melodic/setup.bash: No such file or directory
Works:
=> [stage-2 18/21] RUN ["/bin/bash", "-c", "source /opt/ros/melodic/setup.bash"] 0.5s
=> [stage-2 19/21] [...]
See “/bin/sh: 1: MY_COMMAND: not found” at SuperUser for some more details on how this looks with many lines, or how you would fill the ".bashrc" instead. But that goes a bit beyond the actual question here.
PS: You might also put the commands you want to execute in a single bash script and run that bash script in the Dockerfile (though I would rather put the bash commands in the Dockerfile as well, just my opinion):
#!/bin/bash
set -e
source /opt/ros/melodic/setup.bash

Resources