I have a service that I am bringing up through Rancher via docker-compose. The issue I am running into is that I need to set a password after the container has been deployed.
The way rancher secrets work, is that I set my secret in and rancher will mount a volume on my container with a file containing my secret. I was hoping to be able to execute a script to grab that secret, and set it as a password on my config file.
I don't believe I have a way to get that secret in through the Dockerfile as I don't want the secret to be in git, so I'm left looking at doing it via docker-compose.
Does anyone know if this is possible?
3
This is the way I use for calling a script after a container is started without overriding the entrypoint.
In my example, I used it for initializing the replicaset of my local MongoDB
services:
mongo:
image: mongo:4.2.8
hostname: mongo
container_name: mongodb
entrypoint: ["/usr/bin/mongod","--bind_ip_all","--replSet","rs0"]
ports:
- 27017:27017
mongosetup:
image: mongo:4.2.8
depends_on:
- mongo
restart: "no"
entrypoint: [ "bash", "-c", "sleep 10 && mongo --host mongo:27017 --eval 'rs.initiate()'"]
- In the first part, I simply launch my service (mongo)
- The second service use a "bash" entry point AND a
restart: no<= important
I also use a depends_on between service and setup service for manage the launch order.
3,3411 gold badge28 silver badges41 bronze badges
answered Oct 1, 2020 at 10:02
Gibson LunazizGibson Lunaziz
1,1731 gold badge7 silver badges8 bronze badges
5
The trick is to overwrite the compose COMMAND to perform whatever init action you need before calling the original command.
- Add a script in your image that will perform the init work that you want like set password, change internal config files, etc. Let's call it
init.sh. You add it to your image.
Dockerfile:
FROM: sourceimage:tag
COPY init.sh /usr/local/bin/
ENTRYPOINT []
The above overrides whatever ENTRYPOINT is defined in the sourceimage. That's to make this example simpler. Make sure you understand what the ENTRYPOINT is doing in the Dockerfile from the sourceimage and call it in the command: of the docker-compose.yml file.
docker-compose.yml:
services:
myservice:
image: something:tag
...
command: sh -c "/usr/local/bin/init.sh && exec myexecutable"
It's important to use exec before calling the main command. That will install the command as the first process (PID1) which will make it receive signals like STOP, KILL (Ctrl-C on keyboard) or HUP.
10
You can also use volumes to do this:
services:
example:
image: <whatever>
volume: ./init.sh:/init.sh
entrypoint: sh -c "/init.sh"
Note that this will mount init.sh to the container, not copy it (if that matters, usually it doesn't). Basically processes within the container can modify init.sh and it would modify the file as it exists in your actual computer.
5
Since Compose 2.30 you may use the post_start lifecycle hook.
For your specific usecase, the docker-compose.yml could look like this:
version: '3.8'
services:
my_service:
image: my_image
volumes:
- /run/secrets:/run/secrets # Mount Rancher secrets
post_start:
- command: |
PASSWORD=$(cat /run/secrets/my_secret_file) &&
sed -i "s/placeholder_password/$PASSWORD/" /path/to/config.file
1
docker-compose specify how to launch containers, not how to modify an existing running container.
The Rancher documentation mentions that, for default usage of secrets, you can reference the secret by name in the secrets array in the docker-compose.yml.
The target filename will be the same name as the name of the secret.
By default, the target filename will be created as User ID and Group ID 0, and File Mode of 0444.
Setting external to true in the secrets part will make sure it knows the secret has already been created.Example of a basic
docker-compose.yml:
version: '2'
services:
web:
image: sdelements/lets-chat
stdin_open: true
secrets:
- name-of-secret
labels:
io.rancher.container.pull_image: always
secrets:
name-of-secret:
external: true
As illustrated in "How to Update a Single Running docker-compose Container", updating a container would involve a "build, kill, and up" sequence.
docker-compose up -d --no-deps --build <service_name>
0
Can we execute a script as root during docker container startup?
COPY <<'DASH' /etc/rc.local
set -x
printenv
DASH
ENTRYPOINT ["dash", "-xc", ". /etc/rc.local && exec <the original entrypoint> \"$@\"", "$@"]
CMD [<the original cmd in exec form>]
Explain:
The file
/etc/rc.localis a historical filename for putting scripts that will be executed by pre-systemd-era-daemon SysV init during the system boot.Another similar path for this purpose is
/etc/init.d/*.Here we just take this filename for convention as in docker container there's no init/systemd daemon by default and the
ENTRYPOINTis thepid 1.The original value of image
ENTRYPOINTcan be found in its Dockerfile or get overrided bycompose.yaml.And setting a new
ENTRYPOINTwill reset the originalCMDto empty string:If
CMDis defined from the base image, settingENTRYPOINTwill resetCMDto an empty value. In this scenario,CMDmust be defined in the current image to have a value.so we have to copy the value of
CMDfrom the Dockerfile of original image orcompose.yamlif get overrided in it.sh -xc 'echo "$@"' 1 2 3is a way to pass shell arguments intosh -c, and this example shall runecho 1 2 3that can be verified byset -x.dashis yet another implement ofshell that's faster thanbashand being used as the default/bin/shin Debian.If you use some bashism features that in
/etc/rc.local, feel free to replace it withbashor other shell implements.$@is the value of all shell arguments except the first one like$argv[0]or$0which is the value being passed to execv.In the shell env of
ENTRYPOINTwhen a container is created, its$@will be the value of DockerfileCMD, so we could pass the valueCMDfrom outer shell into the inner that created bysh -c 'echo "$@"' $@.The value of
ENTRYPOINTandCMDin Dockerfile orcompose.yamlmust be written in exec form for removing the extra["sh", "-c"]being prepend to the value when using shell form.- Using
to view the value ofdocker image inspect <compose project name>-<compose service name> \ | jq '.[0].Config | with_entries(select([.key] | inside(["Cmd", "Entrypoint"])))'ENTRYPOINTorCMDof a built image for compose service that can be found indocker images -a. - Whereas the
<compose project name>should be the value of$COMPOSE_PROJECT_NAMEthat defaults thedirname(1)of the path ofcompose.yaml. - The
jqexpression just like_.pick()in lodash.
- Using
Double-quoting
$@as"$@"will prevent shellIFS=word splitting for passing the original DockerfileCMDas is into$1ofsh -c.This can be verified by
for word in "$@"; do echo "$word"; donein/etc/rc.local "$@"and can fix some issues likenginx: invalid option: "off"with the offical docker imagenginx.Prepend
exec(1p)before"$@"will replace theENTRYPOINTprocessdashwith the first one in$@array.This is a common pattern with docker entrypoint to allow passing UNIX signal to the proper process as only the topmost
initprocess, that either to be theENTRYPOINTor be replaced byexecinENTRYPOINT, can recive UNIX signal from docker daemon. Or you will have to write a signal processor withtrapin the entrypoint shell or withsignal()in the entrypoint process.Most
ENTRYPOINTshell in well-formed Dockerfile has already anexec "$@"in the end so thepid 1will be replaced by twice, and thisexeccan still act as a safety net.The dot
.before/etc/rc.localis the whatsourcealias to in bashism. Comparing to execute the/etc/rc.localdirectly without prepending., sourcing it won't requirechmod +xand can passexported enviornment variables into thedashas entrypoint.
Taking the offical docker image php as a example:
We can find its original ENTRYPOINT is docker-php-entrypoint and original CMD is php-fpm, so we should fill them with:
ENTRYPOINT ["dash", "-xc", ". /etc/rc.local && exec docker-php-entrypoint \"$@\"", "$@"]
CMD ["php-fpm"]
If the order of executing script before or after the entrypoint get started is not important for you, also try the much simpler post-start lifecycle hook in Docker Compose.