How to update /etc/hosts file in Docker image during "docker build" - docker

I want to update my /etc/hosts file during "docker build".
I added below line in Dockerfile but it's neither updating /etc/hosts file nor giving any error.
RUN echo "192.168.33.11 mynginx" >> /etc/hosts
I need to update /etc/hosts. Can anyone suggest on this?

With a more recent version of docker, this could be done with docker-compose and its extra_hosts directive
Add hostname mappings.
Use the same values as the docker run client --add-host parameter (which should already be available for docker 1.8).
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"
In short: modify /etc/hosts of your container when running it, instead of when building it.
With Docker 17.x+, you have a docker build --add-host mentioned below, but, as commented in issue 34078 and in this answer:
The --add-host feature during build is designed to allow overriding a host during build, but not to persist that configuration in the image.
The solutions mentioned do refer the docker-compose I was suggesting above:
Run an internal DNS; you can set the default DNS server to use in the daemon; that way every container started will automatically use the configured DNS by default
Use docker compose and provide a docker-compose.yml to your developers.
The docker compose file allows you to specify all the options that should be used when starting a container, so developers could just docker compose up to start the container with all the options they need to set.

You can not modify the host file in the image using echo in RUN step because docker daemon will maintain the file(/etc/hosts) and its content(hosts entry) when you start a container from the image.
However following can be used to achieve the same:
ENTRYPOINT ["/bin/sh", "-c" , "echo 192.168.254.10 database-server >> /etc/hosts && echo 192.168.239.62 redis-ms-server >> /etc/hosts && exec java -jar ./botblocker.jar " ]
Key to notice here is the use of exec command as docker documentation suggests. Use of exec will make the java command as PID 1 for the container. Docker interrupts will only respond to that.
See https://docs.docker.com/engine/reference/builder/#entrypoint

I think docker recently added the --add-host flag to docker build which is really great.
[Edit]
So this feature was updated on 17.04.0-ce
For more detail on how to use docker build with the --add-host flag please visit: https://docs.docker.com/edge/engine/reference/commandline/build/

Since this still comes up as a first answer in Google I'll contribute possible solution.
Command taken from here suprisingly worked for me (Docker 1.13.1, Ubuntu 16.04) :
docker exec -u 0 <container-name> /bin/sh -c "echo '<ip> <name>' >> /etc/hosts"

You can do with the following command at the time of running docker
docker run [OPTIONS] --add-host example.com:127.0.0.1 <your-image-name>:<your tag>
Here I am mapping example.com to localhost 127.0.0.1 and its working.

If this is useful for anyone, the HOSTALIASES env variable worked for me:
echo "fakehost realhost" > /etc/host.aliases
export HOSTALIASES=/etc/host.aliases

You can use the --add-host option during docker run.
For your case use:
docker run --add-host mynginx:192.168.33.11 [image_name]:[tag]
This will update your /etc/hosts
you can check it by using following commands:
docker exec -it [container_id] sh
if sh doesnt work for you ,then you can try bash or /bin/sh or /bin/bash
cat /etc/hosts

I just created a sh script and run it on start docker.
In this script I start all the services and update the hosts file:
on Dockerfile:
CMD /tmp/init_commands.sh & sleep infinity
init_comands.sh
any other commands...
echo "192.168.11.5 XXXXXXX" >> /etc/hosts
echo "192.168.11.6 XXXXXXY" >> /etc/hosts
echo "192.168.11.7 XXXXXXZ" >> /etc/hosts

Following worked for me by mounting the file during docker run instead of docker build
docker service create --name <name> --mount type=bind,source=/etc/hosts,dst=/etc/hosts <image>

Tis is me Dockefile
FROM XXXXX
ENV DNS_1="10.0.0.1 TEST1.COM"
ENV DNS_1="10.0.0.1 TEST2.COM"
CMD ["bash","change_hosts.sh"]`
#cat change_hosts.sh
su - root -c "env | grep DNS | akw -F "=" '{print $2}' >> /etc/hosts"
info
user must su

Just a quick answer to run your container using:
docker exec -it <container name> /bin/bash
once the container is open:
cd ..
then
`cd etc`
and then you can
cat hosts
or:
apt-get update
apt-get vim
or any editor you like and open it in vim, here you can modify say your startup ip to 0.0.0.0

Related

Dokcer - Changing permissions of /etc/hosts in docker file gives error [duplicate]

I want to update my /etc/hosts file during "docker build".
I added below line in Dockerfile but it's neither updating /etc/hosts file nor giving any error.
RUN echo "192.168.33.11 mynginx" >> /etc/hosts
I need to update /etc/hosts. Can anyone suggest on this?
With a more recent version of docker, this could be done with docker-compose and its extra_hosts directive
Add hostname mappings.
Use the same values as the docker run client --add-host parameter (which should already be available for docker 1.8).
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"
In short: modify /etc/hosts of your container when running it, instead of when building it.
With Docker 17.x+, you have a docker build --add-host mentioned below, but, as commented in issue 34078 and in this answer:
The --add-host feature during build is designed to allow overriding a host during build, but not to persist that configuration in the image.
The solutions mentioned do refer the docker-compose I was suggesting above:
Run an internal DNS; you can set the default DNS server to use in the daemon; that way every container started will automatically use the configured DNS by default
Use docker compose and provide a docker-compose.yml to your developers.
The docker compose file allows you to specify all the options that should be used when starting a container, so developers could just docker compose up to start the container with all the options they need to set.
You can not modify the host file in the image using echo in RUN step because docker daemon will maintain the file(/etc/hosts) and its content(hosts entry) when you start a container from the image.
However following can be used to achieve the same:
ENTRYPOINT ["/bin/sh", "-c" , "echo 192.168.254.10 database-server >> /etc/hosts && echo 192.168.239.62 redis-ms-server >> /etc/hosts && exec java -jar ./botblocker.jar " ]
Key to notice here is the use of exec command as docker documentation suggests. Use of exec will make the java command as PID 1 for the container. Docker interrupts will only respond to that.
See https://docs.docker.com/engine/reference/builder/#entrypoint
I think docker recently added the --add-host flag to docker build which is really great.
[Edit]
So this feature was updated on 17.04.0-ce
For more detail on how to use docker build with the --add-host flag please visit: https://docs.docker.com/edge/engine/reference/commandline/build/
Since this still comes up as a first answer in Google I'll contribute possible solution.
Command taken from here suprisingly worked for me (Docker 1.13.1, Ubuntu 16.04) :
docker exec -u 0 <container-name> /bin/sh -c "echo '<ip> <name>' >> /etc/hosts"
You can do with the following command at the time of running docker
docker run [OPTIONS] --add-host example.com:127.0.0.1 <your-image-name>:<your tag>
Here I am mapping example.com to localhost 127.0.0.1 and its working.
If this is useful for anyone, the HOSTALIASES env variable worked for me:
echo "fakehost realhost" > /etc/host.aliases
export HOSTALIASES=/etc/host.aliases
You can use the --add-host option during docker run.
For your case use:
docker run --add-host mynginx:192.168.33.11 [image_name]:[tag]
This will update your /etc/hosts
you can check it by using following commands:
docker exec -it [container_id] sh
if sh doesnt work for you ,then you can try bash or /bin/sh or /bin/bash
cat /etc/hosts
I just created a sh script and run it on start docker.
In this script I start all the services and update the hosts file:
on Dockerfile:
CMD /tmp/init_commands.sh & sleep infinity
init_comands.sh
any other commands...
echo "192.168.11.5 XXXXXXX" >> /etc/hosts
echo "192.168.11.6 XXXXXXY" >> /etc/hosts
echo "192.168.11.7 XXXXXXZ" >> /etc/hosts
Following worked for me by mounting the file during docker run instead of docker build
docker service create --name <name> --mount type=bind,source=/etc/hosts,dst=/etc/hosts <image>
Tis is me Dockefile
FROM XXXXX
ENV DNS_1="10.0.0.1 TEST1.COM"
ENV DNS_1="10.0.0.1 TEST2.COM"
CMD ["bash","change_hosts.sh"]`
#cat change_hosts.sh
su - root -c "env | grep DNS | akw -F "=" '{print $2}' >> /etc/hosts"
info
user must su
Just a quick answer to run your container using:
docker exec -it <container name> /bin/bash
once the container is open:
cd ..
then
`cd etc`
and then you can
cat hosts
or:
apt-get update
apt-get vim
or any editor you like and open it in vim, here you can modify say your startup ip to 0.0.0.0

Docker logging to container

I'm fresh user of Docker. The fist problem with which I'm faced is logging into container.
I'm found solutions to execute container bash commands by
docker exec -it ID bash
But, this is solution only for install/ remove packages. What to use if I want to edit nginx config in docker container ?
One of solutions can be loggin to container via ssh connection, but maybe Docker have something own for this ?, I mean easilly access without install OpenSSH ?
as you said,
docker exec -it container_id bash
and then use your favorite editor to edit any nginx config file. vi or nano is usually installed, but you may need to install emacs or vim, if this is your favorite editor
if you have just a few characters to modify,
docker exec container_id sed ...
might do the job. If you want to SSH into your container, you will need to install SSH and deal with the SSH keys, I am not sure this is what you need.
You're going about it the wrong way. You should rarely need to log into a container to edit files.
Instead, mount the nginx.conf with -v from the host. That way you can edit the file with your normal editor. Once you've got the config working the way you want it, you can then build a new image with it baked in.
In general, you have to get into the mindset of containers being ephemeral. You don't patch them; you throw them away and replace them with a fixed version.
How: Docker logging to container
Yes, you can. You can login the running container.
Exist docker exec or docker attach is not good enough. Looking to start a shell inside a Docker container? The solution is: jpetazzo/nsenter with two commands: nsenter and docker-enter.
If you are in Linux environment, then run below command:
docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter
docker ps
# replace <container_name_or_ID> with real container name or ID.
PID=$(docker inspect --format {{.State.Pid}} <container_name_or_ID>)
nsenter --target $PID --mount --uts --ipc --net --pid
Then you are in that running container, you can run any linux commands now.
I prefer the other command docker-enter. Without login the container, you can directly run linux commands in container with docker-enter command. Second, I can't memory multiple options of nsenter command and no need to find out the container's PID.
docker-enter 0e8c248982c5 ls /opt
If you are mac or windows user, run docket with toolbox:
docker-machine ssh default
docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter
PID=$(docker inspect --format {{.State.Pid}} 0e8c248982c5)
sudo nsenter --target $PID --mount --uts --ipc --net --pid
If you are mac or windows user, run docket with boot2docker:
boot2docker ssh
docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter
PID=$(docker inspect --format {{.State.Pid}} 0e8c248982c5)
sudo nsenter --target $PID --mount --uts --ipc --net --pid
Note: The command docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter only need run one time.
How: edit nginx config
For your second question, you can think about ONBUILD in Docker.
ONBUILD COPY nginx.conf /etc/nginx/nginx.conf
With this solution, you can:
edit nginx.conf in local, you can use any exist editor .
needn't build your image every time after you change nginx configuration.
every time, after you change nginx.conf file in local, you need stop, remove and re-run the containe, new nginx.conf file will be deployed into contrainer when docker run command.
You can refer the detail on how to use ONBUILD here: docker build

What is the difference b/w "service docker start" and "docker -d"?

I'm new to docker, and want to restart docker daemon. I want to add the OPTS to start docker like:
docker --registry-mirror=http://<my-docker-mirror-host> -d
I want to know what is they difference? Does they start the same thing?
By the way, I just use above command in my boot2docker, it did't work at all.
if you use service docker start then it will start docker as service with docker's upstart configuration file, e.g. /etc/default/docker for ubuntu and /etc/sysconfig/docker for centos.
if you use docker -d it will run docker in daemon mode.
if you want define your own registry-mirror for docker, you can do this:
ubuntu
$ echo "DOCKER_OPTS=\"\$DOCKER_OPTS --registry-mirror=http://<my-docker-mirror-host>\"" | sudo tee -a /etc/default/docker
$ sudo service docker restart
centos
sudo sed -i 's|other_args=|other_args=--registry-mirror=http://<my-docker-mirror-host> |g' /etc/sysconfig/docker
sudo sed -i "s|OPTIONS='|OPTIONS='--registry-mirror=http://<my-docker-mirror-host> |g" /etc/sysconfig/docker
sudo service docker restart
mac
boot2docker up
boot2docker ssh "echo $'EXTRA_ARGS=\"--registry-mirror=http://<my-docker-mirror-host>\"' | sudo tee -a /var/lib/boot2docker/profile && sudo /etc/init.d/docker restart”
then your docker service with run with your own registry mirror.
To answer your questions (which are valid for debian/ubuntu, I don't have tinylinux handy to test which is used by boot2docker):
service docker start will run a startup script in /etc/init.d/docker
docker -d is the manual version of the previous script, useful when you want to run docker in debug mode. I suspect the example you gave will not do the same thing, because there are more options specified in the service script.
if you want to add more docker command options, edit the /etc/default/docker file
Update after OP's comments:
To add your new switch, you need to specifically edit the variable (which maybe exported) DOCKER_OPTS and add your option to the end of the existing options.
My /etc/default/docker options are:
export DOCKER_OPTS="--tlsverify --tlscacert=/etc/docker/ca.pem
--tlskey=/etc/docker/server-key.pem --tlscert=/etc/docker/server.pem --label=provider=XXXX
--host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376"
To add the registry-mirror I would edit the DOCKER_OPTS to look like this
export DOCKER_OPTS="--tlsverify --tlscacert=/etc/docker/ca.pem
--tlskey=/etc/docker/server-key.pem --tlscert=/etc/docker/server.pem --label=provider=XXXX
--host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376
--registry- mirror=192.168.59.103:5555"

docker RUN append to /etc/hosts in Dockerfile not working

I have a simple Dockerfile but the first RUN command (to append a host IP address to /etc/hosts) has no effect
FROM dockerfile/java
RUN sudo echo "XX.XX.XXX.XXX some.box.com MyFriendlyBoxName" >> /etc/hosts
ADD ./somejavaapp.jar /tmp/
#CMD java -jar /tmp/somejavaapp.jar
EXPOSE 8280
I build using
docker build .
and then test the RUN echo line has worked using
sudo docker run -t -i <built image ID> /bin/bash
I am then into the container but the /etc/hosts file has not been appended. Running the same echo .... line while now in the container has the desired effect
Can anyone tell me what is wrong with my dockerfile RUN ...?
Docker will generate /etc/hosts dynamically every time you create a new container. So that it can link others. You can use --add-host option:
docker run --add-host www.domain.com:8.8.8.8 ubuntu ping www.domain.com
If you use docker-compose, use extra_hosts:
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"
If you are trying to maintain the hosts file entries between host machine and container another way would be to wrap your command with a shell script that maps your hosts' /etc/hosts into --add-host params:
~/bin/java:
#!/bin/sh
ADD_HOSTS=$(tail -n +10 /etc/hosts | egrep -v '(^#|^$)' | sed -r 's/^([a-z0-9\.\:]+)\s+(.+)$/--add-host="\2:\1"/g')
eval docker run \
-it \
--rm \
$ADD_HOSTS \
<image> \
java $*
return $?
Obviously replace java with whatever you are trying to do...
Explanation; ADD_HOSTS will take everything after the first 10 lines in your hosts' /etc/hosts file | remove comments and blank lines | re-order the entries into --add-host params.
The reason for taking everything after the first 10 lines is to exclude the localhost and ipv6 entries for your host machine. You may need to adjust this to suit your needs.
I had such a problem that I wanted to add multiple domain names for a single IP. Actually, there is no way to make in build time because at each run of the image the /etc/hosts will be overwritten. So, it seems that utilizing --add-host option or its extra_hosts aliens in the docker-compose method be the only way.
But as a matter of fact, adding each of the domain names by a single --add-host was so messy to me. I tried below one and it works:
docker run --add-host "some.box.com MyFriendlyBoxName":X.X.X.X ubuntu cat /etc/hosts
Actually, it just copies and pastes the String after the --add-host to the colon.

How to edit Docker container files from the host?

Now that I found a way to expose host files to the container (-v option) I would like to do kind of the opposite:
How can I edit files from a running container with a host editor?
sshfs could probably do the job but since a running container is already some kind of host directory I wonder if there is a portable (between aufs, btrfs and device mapper) way to do that?
The best way is:
$ docker cp CONTAINER:FILEPATH LOCALFILEPATH
$ vi LOCALFILEPATH
$ docker cp LOCALFILEPATH CONTAINER:FILEPATH
Limitations with $ docker exec: it can only attach to a running container.
Limitations with $ docker run: it will create a new container.
Whilst it is possible, and the other answers explain how, you should avoid editing files in the Union File System if you can.
Your definition of volumes isn't quite right - it's more about bypassing the Union File System than exposing files on the host. For example, if I do:
$ docker run --name="test" -v /volume-test debian echo "test"
The directory /volume-test inside the container will not be part of the Union File System and instead will exist somewhere on the host. I haven't specified where on the host, as I may not care - I'm not exposing host files, just creating a directory that is shareable between containers and the host. You can find out exactly where it is on the host with:
$ docker inspect -f "{{.Volumes}}" test
map[/volume_test:/var/lib/docker/vfs/dir/b7fff1922e25f0df949e650dfa885dbc304d9d213f703250cf5857446d104895]
If you really need to just make a quick edit to a file to test something, either use docker exec to get a shell in the container and edit directly, or use docker cp to copy the file out, edit on the host and copy back in.
We can use another way to edit files inside working containers (this won't work if container is stoped).
Logic is to:
-)copy file from container to host
-)edit file on host using its host editor
-)copy file back to container
We can do all this steps manualy, but i have written simple bash script to make this easy by one call.
/bin/dmcedit:
#!/bin/sh
set -e
CONTAINER=$1
FILEPATH=$2
BASE=$(basename $FILEPATH)
DIR=$(dirname $FILEPATH)
TMPDIR=/tmp/m_docker_$(date +%s)/
mkdir $TMPDIR
cd $TMPDIR
docker cp $CONTAINER:$FILEPATH ./$DIR
mcedit ./$FILEPATH
docker cp ./$FILEPATH $CONTAINER:$FILEPATH
rm -rf $TMPDIR
echo 'END'
exit 1;
Usage example:
dmcedit CONTAINERNAME /path/to/file/in/container
The script is very easy, but it's working fine for me.
Any suggestions are appreciated.
There are two ways to mount files into your container. It looks like you want a bind mount.
Bind Mounts
This mounts local files directly into the container's filesystem. The containerside path and the hostside path both point to the same file. Edits made from either side will show up on both sides.
mount the file:
❯ echo foo > ./foo
❯ docker run --mount type=bind,source=$(pwd)/foo,target=/foo -it debian:latest
# cat /foo
foo # local file shows up in container
in a separate shell, edit the file:
❯ echo 'bar' > ./foo # make a hostside change
back in the container:
# cat /foo
bar # the hostside change shows up
# echo baz > /foo # make a containerside change
# exit
❯ cat foo
baz # the containerside change shows up
Volume Mounts
mount the volume
❯ docker run --mount type=volume,source=foovolume,target=/foo -it debian:latest
root#containerB# echo 'this is in a volume' > /foo/data
the local filesystem is unchanged
docker sees a new volume:
❯ docker volume ls
DRIVER VOLUME NAME
local foovolume
create a new container with the same volume
❯ docker run --mount type=volume,source=foovolume,target=/foo -it debian:latest
root#containerC:/# cat /foo/data
this is in a volume # data is still available
syntax: -v vs --mount
These do the same thing. -v is more concise, --mount is more explicit.
bind mounts
-v /hostside/path:/containerside/path
--mount type=bind,source=/hostside/path,target=/containerside/path
volume mounts
-v /containerside/path
-v volumename:/containerside/path
--mount type=volume,source=volumename,target=/containerside/path
(If a volume name is not specified, a random one is chosen.)
The documentaion tries to convince you to use one thing in favor of another instead of just telling you how it works, which is confusing.
Here's the script I use:
#!/bin/bash
IFS=$'\n\t'
set -euox pipefail
CNAME="$1"
FILE_PATH="$2"
TMPFILE="$(mktemp)"
docker exec "$CNAME" cat "$FILE_PATH" > "$TMPFILE"
$EDITOR "$TMPFILE"
cat "$TMPFILE" | docker exec -i "$CNAME" sh -c 'cat > '"$FILE_PATH"
rm "$TMPFILE"
and the gist for when I fix it but forget to update this answer:
https://gist.github.com/dmohs/b50ea4302b62ebfc4f308a20d3de4213
If you think your volume is a "network drive", it will be easier.
To edit the file located in this drive, you just need to turn on another machine and connect to this network drive, then edit the file like normal.
How to do that purely with docker (without FTP/SSH ...)?
Run a container that has an editor (VI, Emacs). Search Docker hub for "alpine vim"
Example:
docker run -d --name shared_vim_editor \
-v <your_volume>:/home/developer/workspace \
jare/vim-bundle:latest
Run the interactive command:
docker exec -it -u root shared_vim_editor /bin/bash
Hope this helps.
I use sftp plugin from my IDE.
Install ssh server for your container and allow root access.
Run your docker container with -p localport:22
Install from your IDE a sftp plugin
Example using sublime sftp plugin:
https://www.youtube.com/watch?v=HMfjt_YMru0
The way I am doing is using Emacs with docker package installed. I would recommend Spacemacs version of Emacs. I would follow the following steps:
1) Install Emacs (Instruction) and install Spacemacs (Instruction)
2) Add docker in your .spacemacs file
3) Start Emacs
4) Find file (SPC+f+f) and type /docker:<container-id>:/<path of dir/file in the container>
5) Now your emacs will use the container environment to edit the files
docker run -it -name YOUR_NAME IMAGE_ID /bin/bash
$>vi path_to_file
The following worked for me
docker run -it IMAGE_NAME /bin/bash
eg. my image was called ipython/notebook
docker run -it ipython/notebook /bin/bash

Resources