Using docker-compose to set containers timezones - docker

I have a docker-compose file running a few Dockerfiles to create my containers. I don't want to edit my Dockerfiles to set timezones because they could change at any time by members of my team and I have a docker-compose.override.yml file to make local environment changes. However, one of my containers (a Selenium based one) seems to not pull host time zone and that causes problems for me. Based on that I want to enforce timezones on all my containers. In my Dockerfiles right now I do
ENV TZ=America/Denver
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
And everything works fine. How do I replicate the same command in docker-compose syntax?

This is simple solution:
environment:
- TZ=America/Denver

version "2"
services:
serviceA:
...
environment:
TZ: "America/Denver"
command: >
sh -c "ln -snf /usr/share/zoneinfo/$TZ /etc/localtime &&
echo $TZ > /etc/timezone &&
exec my-main-application"
Edit: The question didn't ask for it but I've just added exec my-main-application to show how the main process would be specified. exec is important here to make sure that my-main-application receives Ctrl-C (SIGINT/SIGKILL).

The easiest solution would be share volume in docker-compose.yml like this
ipchanger:
image: codertarasvaskiv/ipchanger:raspberry
volumes:
- "/etc/localtime:/etc/localtime:ro"
ro - means container can only read from /etc/localtime of host-machine.

version: '3.6'
services:
mysql:
image: mysql:5.6
restart: always
container_name: dev-mysql
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
restart: always
environment:
- TZ=Asia/Shanghai
- MYSQL_ROOT_PASSWORD=password # set the root password
ports:
- '3306:3306'
volumes:
- "/etc/localtime:/etc/localtime:ro"
- "/etc/timezone:/etc/timezone:ro"
This my docker-compose.yaml of mysql
Reminder,you need recreate container,other than restart.
if you change the yaml need to recreate
docker-compose -f docker-compose.yaml stop
docker-compose -f docker-compose.yaml rm
docker-compose -f docker-compose.yaml start

version: '2'
services:
ServiceA:
image: image:
- '/etc/localtime:/etc/localtime:ro'

Got the same problem on my Docker engine using Linux containers.
Resolved it by adding a script for installing tzdata package and setting the time zone to final stage of DockerFile for all targeted services using dated processes. The time zone is shared between all images like below:
.env file
COMMON_TIME_ZONE=Europe/Paris
docker-compose.yml
services:
matfront:
build:
context: ./my-app
args:
- TIME_ZONE=$COMMON_TIME_ZONE
Dockerfile
FROM tomcat:9.0
ARG TIME_ZONE
RUN rm -rf /usr/local/tomcat/webapps/*
COPY --from=maven /tmp/ApiDemo/target/ApiDemo.war /usr/local/tomcat/webapps
### Here is the script block that defines the desired timezone ###
# Get the package tzdata that is not installed by default, then set the defined TZ
RUN apt-get install tzdata
RUN ln -snf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime && echo ${TIME_ZONE} > /etc/timezone
EXPOSE 8080
CMD ["/usr/local/tomcat/bin/catalina.sh", "run"]

Related

Container exits from docker-compose up but I can run it manually

Well, basically I got this docker-compose.yml:
version: "3.9"
services:
# Database
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
- ./schemas/mysql.sql:/data/application/init.sql
restart: always
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: 123
MYSQL_ROOT_HOST: 10.5.0.1
MYSQL_DATABASE: forgottenserver
MYSQL_PASSWORD: 123
command: --init-file /data/application/init.sql
networks:
tibia:
ipv4_address: 10.5.0.5
# phpmyadmin
phpmyadmin:
depends_on:
- db
image: phpmyadmin
restart: always
ports:
- "8090:80"
environment:
PMA_HOST: db
MYSQL_ROOT_PASSWORD: 123
networks:
tibia:
ipv4_address: 10.5.0.3
networks:
tibia:
driver: bridge
ipam:
config:
- subnet: 10.5.0.0/16
gateway: 10.5.0.1
volumes:
db_data:
and this Dockerfile:
FROM ubuntu:20.04#sha256:bffb6799d706144f263f4b91e1226745ffb5643ea0ea89c2f709208e8d70c999
ENV TZ=America/Sao_Paulo
ENV WD=/home/tibia/server
ARG DEBIAN_FRONTEND=noninteractive
RUN useradd --system --create-home --shell /bin/bash --gid root --groups sudo --uid 1001 tibia
RUN apt-get update -y && \
apt-get upgrade -y && \
apt-get install --no-install-recommends -y tzdata \
autoconf automake pkg-config build-essential cmake \
liblua5.1-0-dev libsqlite3-dev libmysqlclient-dev \
libxml2-dev libgmp3-dev libboost-filesystem-dev \
libboost-regex-dev libboost-thread-dev
USER tibia
WORKDIR $WD
COPY . .
RUN mv config.lua.dist config.lua && \
mkdir build && \
cd build && \
cmake .. && \
make -j$(grep processor /proc/cpuinfo | wc -l)
EXPOSE 7171 7172
CMD ["/bin/bash"]
The Dockerfile is just building an executable.
The problem is that if I add this to the compose file and try to run all those services, the one that uses the Dockerfile just exits and doesn't restart:
# ...
services:
server:
build: .
ports:
- "7171:7171"
- "7172:7172"
networks:
tibia:
ipv4_address: 10.5.0.4
But if I run the compose with just the services db and phpmyadmin, and then run manually my built image from Dockerfile using:
docker run -itd --network=3777_tibia --ip 10.5.0.4 -p 7171:7171 -p 7172:7172 3777_server
Then it works like a charm!!!! Even the network does work.
Some screenshots of my Docker Desktop:
How can I make this missing service work with the docker-compose file?
NEW EDIT:
image of the logs:
Your dockerfile specifies bash as the command to run.
When you run it via the docker-compose file, bash sees that there's no TTY and it exits immediately and the container stops.
When you run it from the command line, you attach a TTY using the -it options. Bash then runs interactively and waits for input.
To get your container to run interactively when run from docker-compose, you need to add stdin_open and tty options, like this
services:
server:
build: .
ports:
- "7171:7171"
- "7172:7172"
stdin_open: true
tty: true
networks:
tibia:
ipv4_address: 10.5.0.4
Your Dockerfile specifies bash as the command to run. It doesn't actually run the program you built. Since Compose is oriented towards running multiple long-running service-type containers, it's tricky to interact with an interactive shell as the main container process. You also don't usually want to start a container, then start the thing the container does; you just want to start the container and have it run the process.
Once you've built the program, set the image's CMD to run it.
CMD ["./the_program"]
With a typical C(++) program built using Make, you should be able to make install it into /usr/local where you can run it without specifying a path explicitly. You could combine this with a multi-stage build to get a much smaller image without any of the build tools or header files.

gitlab variable is not accessible in the docker-compose.yml file

I am trying to create a CI/CD pipeline using gitlab and now facing an issue with the gitlab variable. This is not accessible inside docker compose file.
this is my gitlab ci yml file
step-production:
stage: production
before_script:
- export APP_ENVIRONMENT="$PRODUCTION_APP_ENVIRONMENT"
only:
- /^release.*$/
tags:
- release-tag
script:
- echo production env value is "$PRODUCTION_APP_ENVIRONMENT"
- sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
- sudo chmod +x /usr/local/bin/docker-compose
- sudo docker-compose -f docker-compose.prod.yml build --no-cache
- sudo docker-compose -f docker-compose.prod.yml up -d
when: manual
and this is my docker compose file
version: "3"
services:
redis:
image: redis:latest
app:
build:
context: .
environment:
- APP_ENVIRONMENT=${PRODUCTION_APP_ENVIRONMENT}
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./app:/app
ports:
- "8000:8000"
restart: on-failure:5
# network_mode: "host"
Can someone help me on how to access the gitlab variable inside docker compose file ? I have spend more than a day on the same issue
The issue has been resolved by the following method
Edit the following line in gitlab ci yml file
sudo docker-compose -f docker-compose.prod.yml build --build-arg DB_NAME=$DEVELOPMENT_DB_NAME --build-arg DB_HOST=$DEVELOPMENT_DB_HOST --no-cache
Define the value of $DEVELOPMENT_DB_NAME and $DEVELOPMENT_DB_HOST in gitlab variables section
In the Docker file, add ARG and ENV sections as follows
ARG DB_NAME
ARG DB_HOST
ENV DB_NAME=${DB_NAME}
ENV DB_HOST=${DB_HOST}
Make sure that no environment variables with the same name are not defined in the docker-compose yml file
That's it !!!

Docker volume is not fully sync directory for one container

I've created simple project for Symfony4 based on php7.3+mariadb via docker-compose. I used Docker for Windows 10 (x64)
It works correctly at one machine but at laptop it doesn't sync correctly with container.
In root folder I have standard Symfony structure with docker files like:
- /config
- /public
- /src
....
- /env
- /docker
- .env
- docker-compose.yaml
...
My actions in Git Bash to start app:
docker-compose build
it works correctly, all actions were finished successfully
docker-compose up -d
it works correctly, both containers run successfully
docker-compose exec app bash
works correctly, console starts
ls
result is docker env
it syncs only 2 directories - docker and env
docker dir was synced not in full mode - only subdirectories structure without files
I tried to detect what reason can be for problem with files sync but I haven't enough knowledge and experience with Docker. docker-compose logs have no errors.
Maybe somebody can help how to detect the reason? It starts once time but after reboot problem occurs again...
docker-compose.yaml:
version: '3'
services:
app:
restart: unless-stopped
build:
context: .
dockerfile: docker/webserver-apache/Dockerfile
image: php:7.3.1-apache-stretch
volumes:
- "./docker/webserver-apache/sites-enabled:/etc/apache2/sites-enabled:ro"
- "./:/var/www/html"
ports:
- 8080:80
networks:
- dphptrainnet
mariadb:
restart: unless-stopped
image: mariadb:10.4.1
networks:
- dphptrainnet
volumes:
- ./env/mariadb/data:/var/lib/mysql
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD}
networks:
dphptrainnet:
Dockerfile:
FROM php:7.3.1-apache-stretch
# Setting up constants for an environment
ENV PHP_MEMORY_LIMIT 512M
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
php composer-setup.php && \
php -r "unlink('composer-setup.php');" && \
mv composer.phar /usr/local/bin/composer
RUN apt-get update && \
apt-get install -y curl vim git zip unzip
# Setting up httpd issues
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
RUN a2enmod rewrite headers && /etc/init.d/apache2 restart
RUN echo "127.0.0.1 dockertrain.local" >> /etc/hosts
WORKDIR "/var/www/html"
RUN a2enmod rewrite
I've found only one working solution - reshare drive for Docker:
1. Disable shared disk, click Apply
2. Enable shared disk, click Apply
3. Restart application - files were synced
But how I should detect there any problems with drive access? No errors, no logs....

Test my go app in a container launched by docker compose with Gitlab CI

I have a Golang app, that depends a FTP Server.
So, In docker compose, I build a FTP service and I refer to it into my tests.
So, in my docker-compose.yml I have:
version: '3'
services:
mygoapp:
build:
dockerfile: ./Dockerfile.local
context: ./
volumes:
- ./volume:/go
- ./test_files:/var/test_files
networks:
mygoapp_network:
env_file:
- test.env
tty: true
ftpd-server:
container_name: ftpd-server
image: stilliard/pure-ftpd:hardened
environment:
PUBLICHOST: "0.0.0.0"
FTP_USER_NAME: "julien"
FTP_USER_PASS: "test"
FTP_USER_HOME: "/home/www/julien"
restart: on-failure
networks:
mygoapp_network:
networks:
mygoapp_network:
external: true
In my gitlab-ci.yml I have
variables:
PACKAGE_PATH: /go/src/gitlab.com/xxx
VOLUME_PATH: /var/test_files
stages:
- test
# A hack to make Golang-in-Gitlab happy
.anchors:
- &inject-gopath
mkdir -p $(dirname ${PACKAGE_PATH})
&& ln -s ${CI_PROJECT_DIR} ${PACKAGE_PATH}
&& cd ${PACKAGE_PATH}
test:
image: docker:18
services:
- docker:dind
stage: test
# only:
# - production
before_script:
- touch test.env
- apk update
- apk upgrade
- apk add --no-cache py-pip
- pip install docker-compose
- docker network create mygoapp_network
- mkdir -p volume/log
script:
- docker-compose -f docker-local.yaml up --build -d
- docker exec project-0_mygoapp_1 ls /var/test_files
- docker exec project-0_mygoapp_1 echo $VOLUME_PATH
- docker exec project-0_mygoapp_1 go test ./... -v
All my services are up
But when I run
- docker exec project-0_myapp_1 echo $VOLUME_PATH
I can see $VOLUME_PATH is equal to /var/test_files
but inside code, when I do:
os.Getenv("VOLUME_PATH")
variable is empty
Also, in local, with a docker exec, variable is OK.
I also tried to put Variables into test definition, but it still doesn' work
EDIT: The only way I could do it is setting environment vars in docker compose, but it is not so great
Any idea how to fix it ?
The behaviour of your script is predictable - all environment variables are being expanded when they are met (unless they are in single quotes). So, your line
docker exec project-0_myapp_1 echo $VOLUME_PATH
is expanded before being executed, and $VOLUME_PATH is taken from gitlab runner, not from container.
The only way I see to get this script printing environment variable from inside container is putting script inside sh-file and calling that file:
doit.sh
echo $VOLUME_PATH
gitlab-ci.yml
docker exec project-0_myapp_1 doit.sh

Dockerfile and docker-compose.yaml for different environments

docker-compose for prod:
version: '2'
services:
db:
image: mongo:3
ports:
- "27017:27017"
api-server:
build: .
ports:
- "443:443"
links:
- db
volumes:
- /www/node_modules
Dockerfile for prod:
FROM alpine:3.4
LABEL authors="John Doe"
RUN apk add --update nodejs bash git
COPY package.json /www/package.json
RUN cd /www; apk --no-cache add --virtual builds-deps build-base python && npm install && npm rebuild bcrypt --build-from-source && apk del builds-deps
COPY . /www
WORKDIR /www
ENV PORT 8080
EXPOSE 8080
CMD ["npm", "start"]
docker-compose for dev:
version: '2'
services:
db:
image: mongo:3
ports:
- "27017:27017"
api-server:
build: .
ports:
- "8080:8080"
links:
- db
volumes:
- .:/www
- /www/node_modules
Dockerfile for dev
FROM alpine:3.4
LABEL authors="John Doe"
RUN apk add --update nodejs bash git
COPY package.json /www/package.json
RUN cd /www; apk --no-cache add --virtual builds-deps build-base python && npm install && npm rebuild bcrypt --build-from-source && apk del builds-deps
WORKDIR /www
ENV PORT 8080
EXPOSE 8080
CMD ["npm", "run", "dev"]
I'm running it with docker-compose up.
Right now i have to manually make changes to files in order to change environment, which is, of course, wrong way to do this.
I assume there should be a way to avoid these manual changes. How do i do that?
You can specify environment in the services part of the docker-compose.yml file.
Example:
services:
environment:
NODE_ENV: "development"
APP_PORT: 5000
DB_URI: "<DB URI>"
And in your code, you can take these values by specifying process.env.NODE_ENV
Dockerfile should contain the commands for creating the image. That image when used with docker-compose api-server service of yours will run the server as required.
For eg., in your case:
Your Dockerfile should look something like this.
FROM alpine:3.4
LABEL authors="John Doe"
RUN apk add --update nodejs bash git
RUN mkdir /www
WORKDIR /www
ADD package.json /www/package.json
RUN apk --no-cache add --virtual builds-deps build-base python && npm install && npm rebuild bcrypt --build-from-source && apk del builds-deps
This will create your image.
regarding your docker-compose.yml file, use two separate docker-compose files for both production and development. Use env files to separate out development and production variables. you can check in the development docker-compose.yml and development env file in your repository, production docker-compose and environment files will be specific to your production server.
Your sample docker-compose.yml file should look something like this
version: '2'
services:
db:
image: mongo:3
ports:
- "27017:27017"
api-server:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
links:
- db
env_file:
development.env
volumes:
- ./:/www
- /www/node_modules # I really don't understand this statement
command: >
/bin/ash -c "npm run dev"
This will be running your development server.
Similarly a same docker-compose.yml file with different ports exposed for production -443:443 in your case and env_file to production.env and command set to /bin/ash -c "npm start" will help run your production server.
version: '2'
services:
db:
image: mongo:3
ports:
- "27017:27017"
api-server:
build:
context: .
dockerfile: Dockerfile
ports:
- "443:443"
links:
- db
env_file:
production.env
volumes:
- ./:/www
- /www/node_modules # I really don't understand this statement
command: >
/bin/ash -c "npm start"
In case you are running the development server and production server (never advisable) in the same machine you can create two files named docker-compose-development.yml and docker-compose-production.yml for development and production systems respectively and then you can run the server by using the command:
sudo docker-compose -f docker-compose-development.yml up
sudo docker-compose -f docker-compose-production.yml up
for development and production system respectively.
you can use environment variables as
for example
set env as export env="prod" in you local machine terminal
and in docker-compose-file
image: container_image_${env} or image: container_image:${env}
will create images as container_image_prod or container_image:prod
you can also set the service name for db as db_${env} so that you get the service name according to the environment as db_prod in this case similarly you can do for other services if required

Resources