How to "dynamize" Dockerfile / Docker Compose? - php

I'm Dockerizing legacy PHP project. I would like to have Xdebug enabled in development environment and my Dockerfile copies pre-built php.ini into container.
Due to some network issues we have to have xdebug.remote_connect_back = 0 on Mac OS X (and corresponding xdebug.remote_host = docker.for.mac.localhost) and xdebug.remote_connect_back = 1 on Linux.
Is it possible to grab current OS type in Dockerfile/Docker Compose to copy php.ini corresponding to host OS?

Use volumes described here in docker-compose.yml. Create php.linux.ini and php.mac.ini in a config folder (or wherever) and map one of them to the container:
services:
php:
image: php
volumes:
- ./config/php.linux.ini:/etc/php.ini #or wherever the config is
Of course your users will have to manually change php.linux.ini for php.mac.ini, but it's a one time manual change.

That information isn't (and shouldn't) be available at image build time. The same Linux-based image could be run on native Linux, a Linux VM on Mac (and then either the Docker Machine VM or the hidden VM provided by Docker for Mac), a Linux VM on Windows, or even a Linux VM on Linux, regardless of where it was originally built.
Configuration such as host names should be provided at container run time. Environment variables are a typical way to do this, or you can use the Docker volume mechanism to push in configuration files from the host.
If your issue is purely around debugging your application, you can also set up a full development environment on your host, and only build in to your image the things you need to run it in a more production-like environment.

I decided to use Docker Compose ability of reading .env files. The whole workflow is as following:
create .env.sample file with all the lines commented (sorry, couldn't manage to correctly display commented lines):
OS=windows
OS=linux
OS=mac
ignore .env file by adding /.env line to .gitignore file
copy sample file with $ cp .env.sample .env and leave uncommented just one line corresponding to your OS
move OS-specific Xdebug-related section of php.ini into separate file with names like xdebug-mac.ini, xdebug-windows.ini, xdebug-linux.ini, etc.
add to docker-compose.yml args section to chosen service with value like - OS=${OS}
in corresponding Dockerfile add lines:
ARG OS=${OS}
COPY ./xdebug-${OS}.ini /usr/local/etc/php/conf.g/
OS value mentioned in .env will be expanded on building image time
execute $ docker-compose up -d --build to build image and start container
commit all your changes on success to let your colleagues have Xdebug set properly on any platform; don't forget to tell them make their own instance of .env file from template

Related

set aws credentails folder for php apache container [duplicate]

I am running docker-container on Amazon EC2. Currently I have added AWS Credentials to Dockerfile. Could you please let me know the best way to do this?
A lot has changed in Docker since this question was asked, so here's an attempt at an updated answer.
First, specifically with AWS credentials on containers already running inside of the cloud, using IAM roles as Vor suggests is a really good option. If you can do that, then add one more plus one to his answer and skip the rest of this.
Once you start running things outside of the cloud, or have a different type of secret, there are two key places that I recommend against storing secrets:
Environment variables: when these are defined on a container, every process inside the container has access to them, they are visible via /proc, apps may dump their environment to stdout where it gets stored in the logs, and most importantly, they appear in clear text when you inspect the container.
In the image itself: images often get pushed to registries where many users have pull access, sometimes without any credentials required to pull the image. Even if you delete the secret from one layer, the image can be disassembled with common Linux utilities like tar and the secret can be found from the step where it was first added to the image.
So what other options are there for secrets in Docker containers?
Option A: If you need this secret only during the build of your image, cannot use the secret before the build starts, and do not have access to BuildKit yet, then a multi-stage build is a best of the bad options. You would add the secret to the initial stages of the build, use it there, and then copy the output of that stage without the secret to your release stage, and only push that release stage to the registry servers. This secret is still in the image cache on the build server, so I tend to use this only as a last resort.
Option B: Also during build time, if you can use BuildKit which was released in 18.09, there are currently experimental features to allow the injection of secrets as a volume mount for a single RUN line. That mount does not get written to the image layers, so you can access the secret during build without worrying it will be pushed to a public registry server. The resulting Dockerfile looks like:
# syntax = docker/dockerfile:experimental
FROM python:3
RUN pip install awscli
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials aws s3 cp s3://... ...
And you build it with a command in 18.09 or newer like:
DOCKER_BUILDKIT=1 docker build -t your_image --secret id=aws,src=$HOME/.aws/credentials .
Option C: At runtime on a single node, without Swarm Mode or other orchestration, you can mount the credentials as a read only volume. Access to this credential requires the same access that you would have outside of docker to the same credentials file, so it's no better or worse than the scenario without docker. Most importantly, the contents of this file should not be visible when you inspect the container, view the logs, or push the image to a registry server, since the volume is outside of that in every scenario. This does require that you copy your credentials on the docker host, separate from the deploy of the container. (Note, anyone with the ability to run containers on that host can view your credential since access to the docker API is root on the host and root can view the files of any user. If you don't trust users with root on the host, then don't give them docker API access.)
For a docker run, this looks like:
docker run -v $HOME/.aws/credentials:/home/app/.aws/credentials:ro your_image
Or for a compose file, you'd have:
version: '3'
services:
app:
image: your_image
volumes:
- $HOME/.aws/credentials:/home/app/.aws/credentials:ro
Option D: With orchestration tools like Swarm Mode and Kubernetes, we now have secrets support that's better than a volume. With Swarm Mode, the file is encrypted on the manager filesystem (though the decryption key is often there too, allowing the manager to be restarted without an admin entering a decrypt key). More importantly, the secret is only sent to the workers that need the secret (running a container with that secret), it is only stored in memory on the worker, never disk, and it is injected as a file into the container with a tmpfs mount. Users on the host outside of swarm cannot mount that secret directly into their own container, however, with open access to the docker API, they could extract the secret from a running container on the node, so again, limit who has this access to the API. From compose, this secret injection looks like:
version: '3.7'
secrets:
aws_creds:
external: true
services:
app:
image: your_image
secrets:
- source: aws_creds
target: /home/user/.aws/credentials
uid: '1000'
gid: '1000'
mode: 0700
You turn on swarm mode with docker swarm init for a single node, then follow the directions for adding additional nodes. You can create the secret externally with docker secret create aws_creds $HOME/.aws/credentials. And you deploy the compose file with docker stack deploy -c docker-compose.yml stack_name.
I often version my secrets using a script from: https://github.com/sudo-bmitch/docker-config-update
Option E: Other tools exist to manage secrets, and my favorite is Vault because it gives the ability to create time limited secrets that automatically expire. Every application then gets its own set of tokens to request secrets, and those tokens give them the ability to request those time limited secrets for as long as they can reach the vault server. That reduces the risk if a secret is ever taken out of your network since it will either not work or be quick to expire. The functionality specific to AWS for Vault is documented at https://www.vaultproject.io/docs/secrets/aws/index.html
The best way is to use IAM Role and do not deal with credentials at all. (see http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html )
Credentials could be retrieved from http://169.254.169.254..... Since this is a private ip address, it could be accessible only from EC2 instances.
All modern AWS client libraries "know" how to fetch, refresh and use credentials from there. So in most cases you don't even need to know about it. Just run ec2 with correct IAM role and you good to go.
As an option you can pass them at the runtime as environment variables ( i.e docker run -e AWS_ACCESS_KEY_ID=xyz -e AWS_SECRET_ACCESS_KEY=aaa myimage)
You can access these environment variables by running printenv at the terminal.
Yet another approach is to create temporary read-only volume in docker-compose.yaml. AWS CLI and SDK (like boto3 or AWS SDK for Java etc.) are looking for default profile in ~/.aws/credentials file.
If you want to use other profiles, you just need also to export AWS_PROFILE variable before running docker-compose command.
export AWS_PROFILE=some_other_profile_name
version: '3'
services:
service-name:
image: docker-image-name:latest
environment:
- AWS_PROFILE=${AWS_PROFILE}
volumes:
- ~/.aws/:/root/.aws:ro
In this example, I used root user on docker. If you are using other user, just change /root/.aws to user home directory.
:ro - stands for read-only docker volume
It is very helpful when you have multiple profiles in ~/.aws/credentials file and you are also using MFA. Also helpful when you want to locally test docker-container before deploying it on ECS on which you have IAM Roles, but locally you don't.
Another approach is to pass the keys from the host machine to the docker container. You may add the following lines to the docker-compose file.
services:
web:
build: .
environment:
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
- AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}
The following one-liner works for me even when my credentials are set up by aws-okta or saml2aws:
$ docker run -v$HOME/.aws:/root/.aws:ro \
-e AWS_ACCESS_KEY_ID \
-e AWS_CA_BUNDLE \
-e AWS_CLI_FILE_ENCODING \
-e AWS_CONFIG_FILE \
-e AWS_DEFAULT_OUTPUT \
-e AWS_DEFAULT_REGION \
-e AWS_PAGER \
-e AWS_PROFILE \
-e AWS_ROLE_SESSION_NAME \
-e AWS_SECRET_ACCESS_KEY \
-e AWS_SESSION_TOKEN \
-e AWS_SHARED_CREDENTIALS_FILE \
-e AWS_STS_REGIONAL_ENDPOINTS \
amazon/aws-cli s3 ls
Please note that for advanced use cases you might need to allow rw (read-write) permissions, so omit the ro (read-only) limitation when mounting the .aws volume in -v$HOME/.aws:/root/.aws:ro
Volume mounting is noted in this thread but as of docker-compose v3.2 + you can Bind Mount.
For example, if you have a file named .aws_creds in the root of your project:
In your service for the compose file do this for volumes:
volumes:
# normal volume mount, already shown in thread
- ./.aws_creds:/root/.aws/credentials
# way 2, note this requires docker-compose v 3.2+
- type: bind
source: .aws_creds # from local
target: /root/.aws/credentials # to the container location
Using this idea, you can publicly store your docker images on docker-hub because your aws credentials will not physically be in the image...to have them associated, you must have the correct directory structure locally where the container is started (i.e. pulling from Git)
You could create ~/aws_env_creds containing:
touch ~/aws_env_creds
chmod 777 ~/aws_env_creds
vi ~/aws_env_creds
Add these value (replace the key of yours):
AWS_ACCESS_KEY_ID=AK_FAKE_KEY_88RD3PNY
AWS_SECRET_ACCESS_KEY=BividQsWW_FAKE_KEY_MuB5VAAsQNJtSxQQyDY2C
Press "esc" to save the file.
Run and test the container:
my_service:
build: .
image: my_image
env_file:
- ~/aws_env_creds
If someone still face the same issue after following the instructions mentioned in accepted answer then make sure that you are not passing environment variables from two different sources. In my case I was passing environment variables to docker run via a file and as parameters which was causing the variables passed as parameters show no effect.
So the following command did not work for me:
docker run --env-file ./env.list -e AWS_ACCESS_KEY_ID=ABCD -e AWS_SECRET_ACCESS_KEY=PQRST IMAGE_NAME:v1.0.1
Moving the aws credentials into the mentioned env.list file helped.
for php apache docker the following command works
docker run --rm -d -p 80:80 --name my-apache-php-app -v "$PWD":/var/www/html -v ~/.aws:/.aws --env AWS_PROFILE=mfa php:7.2-apache
Based on some of previous answers, I built my own as follows.
My project structure:
├── Dockerfile
├── code
│   └── main.py
├── credentials
├── docker-compose.yml
└── requirements.txt
My docker-compose.yml file:
version: "3"
services:
app:
build:
context: .
volumes:
- ./credentials:/root/.aws/credentials
- ./code:/home/app
My Docker file:
FROM python:3.8-alpine
RUN pip3 --no-cache-dir install --upgrade awscli
RUN mkdir /app
WORKDIR /home/app
CMD python main.py

Docker Set environment variables in ubuntu cant access through php getenv

I've created a Docker container with an Ubuntu base image. Setting the environment variables through a .env file. When running the container, I can see the variables being passed through using the shell terminal.
I want to able to get the env varibles in my wp-config. I am using getenv but it is not working..
Any suggestions..
Thanks
You can set the environment variable for your docker container in 2 ways
In docker run command use docker run -e VARIABLE=VALUE ...
In docker-compose file you can set in like:
environment:
- DEBUG=1
https://docs.docker.com/compose/environment-variables/#set-environment-variables-in-containers
You use .env file, so you certainly use docker-compose. If not use docker-compose, .env will not make effect. And the .env file must be placed in the directory where docker-compose is run from.
Whole solution could be something like:
.env
MY_VARIABLE=abc
docker-compose.yml
version: '3'
services:
my_service:
environment:
- MY_VARIABLE="${MY_VARIABLE}"
wp-config.php
echo getenv('MY_VARIABLE');
I guess you did not get env because you did not do - MY_VARIABLE="${MY_VARIABLE}" in docker-compose.yml, the value in .env will not be automatically act as an environment variable to container, you need to handle it in compose file. FYI.
Detail refers to offical guide
Both previous answers, I could already pass the env variables to my apache environment served by docker. I just needed to add Pass env_name to the .htaccess file for each env variable.
I could then get the values via the $SERVER['env_name'] within my php application..

How can I debug php mounted to a container running on docker beta for mac

I have spent half the day trying different things including this solution I came across on gist.github.com
I have a docker-compose file which mounts a folder containing my client and server projects:
volumes:
- ~/projectx:/projectx
my docker php.ini contains the following xdebug options
[xdebug]
xdebug.remote_enable=1
xdebug.remote_port=9000
xdebug.remote_connect_back=On
xdebug.var_display_max_children = 999
xdebug.var_display_max_data = 999
xdebug.var_display_max_depth = 100
I have the xdebug chrome extension installed
In my mac host file I have the following 127.0.0.1 localhost dtest.xxx.com so I can access my web app (and other docker web apps) through dtest.xxx.com which all works to run the app
In Phpstorm (version 2016.1.2) In preferences -> Languages & Frameworks -> PHP -> Servers I have :
host = dtest.xxx.com, port=80 , Debugger = Xdebug Use path mappings is checked and I have
File/Directory set to /Users/<myname>/projectx/server
Absolute path on the server is set to /projectx/server
I have tried setting the xdebug.remote_host to my macs ip obtained from ifconfig as well as trying the ip in /Users/<myname>/Library/Containers/com.docker.docker/Data/database/com.docker.driver.amd64-linux/slirp/host
But I cannot hit a breakpoint , I have even added xdebug_break() double check.
I am also running the latest docker beta Version 1.12.0-rc3-beta18 (build: 9996)
I would very much appreciate anyone's help, it's a real blocker for me.
If any further information is required I am happy to provide.
UPDATE 2:
As pointed out by #Rashidul , Since 17.06 docker for Mac, you can replace xdebug.remote_host="192.168.65.1" with xdebug.remote_host="docker.for.mac.localhost"
UPDATE: The Correct way
so I eventually figured out that my xdebug settings in docker should be
xdebug.enable=1
xdebug.remote_enable = 1
xdebug.idekey="PHPSTORM"
xdebug.remote_port=9000
xdebug.remote_host="192.168.65.1"
xdebug.remote_connect_back=0
where 192.168.65.1 is the entry found in ~/Library/Containers/com.docker.docker/Data/database/com.docker.driver.amd64-linux/slirp/host
In recent update the above no longer exists, to get the Docker host IP I know use the below my .profile
export DOCKER_HOST_IP=$(ipconfig getifaddr en0)
Also make sure to set the idekey correctly in the chrome extension, in my case PHPSTORM
With this I no longer require the ssh tunnel
Original Solution:
The workaround I used was to open up an ssh tunnel to the docker container running php using a command like, in my case I have the docker container ssh port mapped to 12 hence the -p 12
ssh -R 9000:localhost:9000 root#dtest.xxx.com -p:12

Proper usage of Laravel Homestead

I have done few projects with laravel. I also tried to use the good practise such us using only models for all calls and use artisian commands.
In my next project i plan to use still more such as using Homestead.
I read more about Laravel Homestead which actually needs Vagrantup.
But i don't know the actual need of Homestead and how to install it.
Any help would be helpful for me and future readers.
I assume you already install VirtualBox or VMWare. After downloading Homestead from Git, you need to amend Homestead.yaml file
ip: "192.168.10.10"
memory: 2048
cpus: 1
authorize: /Users/Documents/workspace/myssh.pub #pub path
keys:
- /Users/Documents/workspace/myssh #ssh key
folders:
- map: /Users/Documents/workspace/dev/myProject #My project directory
to: /home/vagrant/code/myProject #Vagrant project directory, no need to change this /home/vagrant/code/
sites:
- map: yourproject.app #you can change whatever you prefer
to: /home/vagrant/code/myProject/public #no need to change this /home/vagrant/code/
variables:
- key: APP_ENV
value: local
Then, we remain one final step to amend. Open hosts file under /etc/. I will use VI to amend this. You can use whatever you like.
vi /etc/hosts
You need to add new line in this file
192.168.10.10 yourproject.app //same with code line 11
Finally, you just only need to run vagrant up. Please don't forgot one thing. If you shutdown your PC without shutdown your vagrant, it's take too long to shutdown your PC. Even your PC is shutdown, vagrant might be crushed when you run again vagrant up. Before your shutdown your PC, you need to run vagrant halt for safe.
Hope this help.

Add php and nginx settings to homestead 2.0 provisioning

i would like to increase the default max-post size in the php.ini and max upload size in the nginx config. How can i add that in an .sh file so it get executed when i provision the box?
Use the provision tools, such as puppet, chef, salt, ansible, etc.
For example, put below lines in your Vagrantfile, it will automatically apply puppet modules (such as php and nginx) with your changes.
config.vm.provision :puppet do |puppet|
puppet.module_path = "modules"
puppet.manifests_path = "manifests"
puppet.manifest_file = "vagrant.pp"
puppet.options = ['--verbose']
end
Take a look on these urls.
https://docs.vagrantup.com/v2/provisioning/puppet_apply.html
https://docs.vagrantup.com/v2/provisioning/ansible.html
https://docs.vagrantup.com/v2/provisioning/chef_solo.html
The correct answer to the exact question would be (given the current version of Homestead):
After cloning go to src/stubs and edit the after.sh file
launch init.sh from the root of the repository
vagrant up
after.sh is a file copied to the VM and launched after homestead finishes its provisioning.

Categories