116

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?

Blooze
  • 1,467
  • 4
  • 14
  • 18
  • Absolutely, that's a fairly normal way of setting secrets. Just add the relevant shell script as (or to) your CMD or ENTRYPOINT. – Paul Hicks Dec 03 '17 at 06:06
  • to have access to secret without expose in Dockerfile, you can use .env file with docker-compose : https://docs.docker.com/compose/environment-variables/ – bcag2 Aug 03 '21 at 16:33
  • Hi. This is specific problem. The way to use a separate service as a set UP service is working solution. But often images provide you some sort of the 'hooks' that can be used. Like [here](https://github.com/bitnami/bitnami-docker-kafka/blob/master/3.1/debian-10/rootfs/opt/bitnami/scripts/libkafka.sh#L768) for kafka. You need just put a scripts to the "docker-entrypoint-initdb.d" to run some sort of set up. – stopanko Apr 07 '22 at 17:42

4 Answers4

59

The trick is to overwrite the compose COMMAND to perform whatever init action you need before calling the original command.

  1. 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.

yuяi
  • 2,507
  • 1
  • 21
  • 44
Bernard
  • 14,187
  • 11
  • 62
  • 62
  • 4
    executing this causes `/usr/local/bin/docker-entrypoint.sh: line 172: /usr/local/bin/init.sh: No such file or directory` – James Jordan Taylor Aug 14 '18 at 00:28
  • 3
    Additionally, removing the first part of the command causes the error `exec: not found` – James Jordan Taylor Aug 14 '18 at 00:31
  • Looks like docker isn't treating the `&&` as command chaining. It's treating it and the rest of the line as additional arguments. – Burhan Ali Feb 20 '20 at 11:58
  • 3
    @BurhanAli I've updated the answer to explicitly call the "shell -c". Also, there are many different combinations of `ENTRYPOINT`/`CMD` (Dockerfile) and `entrypoint:`/`command:`(docker compose) which can override each other. To keep this answer concise, I reset the ENTRYPOINT so that it doesn't override `command`. – Bernard Feb 20 '20 at 23:31
  • @Bernard Thank you much more elegant that creating another container – SparkleGoat Dec 31 '20 at 08:55
  • What is `myexecutable` in yml file? I am just trying to run this command as script `redis-server --deamonize yes`. Do i need to use exec? – Murtaza Haji Jun 02 '21 at 17:33
  • 1
    @MurtazaHaji. In your case the line would be `command: sh -c "/usr/local/bin/init.sh && exec redid-server --deamonize yes` – Bernard Jun 07 '21 at 11:25
  • I don't understand the relation between the `command:` and ENTRYPOINT in the sourceimage. If we're using `command:`, then __the CMD__ of the sourceimage _might be_ overridden, not the ENTRYPOINT. Am I missing something? – aderchox Oct 31 '21 at 09:32
  • 2
    @aderchox That's correct, `command` will override the `CMD` instruction in the image. However, the final startup action is obtained by combining `ENTRYPOINT` and `CMD`. So if `ENTRYPOINT` is for instance `["echo"]`, you can set `CMD` to `"hello"`and this will print "hello". I believe this was done historically to allow different arguments to be specified easily. `ENTRYPOINT` contains the main executable and `CMD` contains the arguments passed to the executable. You can also set `ENTRYPOINT` to an empty array so that `CMD` or `command` contains the full startup action line, like we do above. – Bernard Oct 31 '21 at 11:10
52

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.

redgeoff
  • 2,953
  • 1
  • 23
  • 38
Gibson Lunaziz
  • 535
  • 4
  • 8
  • 4
    through the luck of the draw, I'm trying to do the same thing with mongo and stumbled across this answer in this thread. Thanks! – java-addict301 Dec 02 '21 at 18:27
  • This approach seems interesting but for me raises some questions. First, why use the `mongo` image for the `mongosetup` container? Will just any image do? Or is it important that it should be the `mongo` one? Second, you say that setting `restart: no` is important, but why is that? So that setup does not get executed multiple times? Last, I see `sleep 10`? Is that to give mongo time to initialize? Seems tricky to use `sleep` for that... – Stijn de Witt Apr 06 '22 at 07:45
14

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.

Asad-ullah Khan
  • 1,100
  • 15
  • 21
2

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>
VonC
  • 1,129,465
  • 480
  • 4,036
  • 4,755