I've followed suit to quite some instructions, tutorials, and also questions in this forum, but I can still not get this working. I've setup a REST API using a PHP Apache Docker container, and need to create a rewrite rule in my apache config to reroute API requests to index.php (the REST controller). Currently, with what's written below, I'm getting a 404:
File Structure on local machine (listed everything except php source code; not needed here):
php
conf.d
- error_reporting.ini
- xdebug.ini
Dockerfile
index.php
apache
- apache2.conf
docker-compose.yml
The content of the Dockerfile being:
FROM php:8.1-apache
WORKDIR /var/www/html/
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug \
&& a2enmod rewrite
COPY . .
EXPOSE 80
And the content of docker-compose.yml being:
services:
php:
build: ./php
depends_on:
- db
container_name: php-apache
ports:
- 80:80
volumes:
- ./php:/var/www/html
- ./php/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
- ./php/conf.d/error_reporting.ini:/usr/local/etc/php/conf.d/error_reporting.ini
- ./apache/apache2.conf:/etc/apache2/apache2.conf
environment:
MARIADB_HOST: localhost
MARIADB_USER: root
MARIADB_PASSWORD: top_very_secret
MARIADB_DB: apidb
adminer:
image: adminer
depends_on:
- db
restart: always
ports:
- 8080:8080
db:
image: mariadb
container_name: db
volumes:
- maria-db-storage:/var/lib/mysql
environment:
MARIADB_ROOT_PASSWORD: top_very_secret
MARIADB_DATABASE: apidb
volumes:
maria-db-storage:
Regarding the contents of apache2.conf; I've done the following to create it on the local machine:
Went into the container's virtual filesystem using docker exec -t -i <container_name> /bin/bash.
Wandered to /etc/apache2
Printed the file contents via cat apache2.conf
Copy-pasted the contents into my local /apache/apache2.conf file
Added the following directive lines to the end of that local file:
# Custom directives start here
# Set Server's name
ServerName 'localhost'
# Rewrite for routing of all requests through REST controller
RewriteEngine On
# If requested resource is index.php, do nothing
RewriteRule ^index\.php$ - [L]
# If requested resource is a file or directory that does not exist, reroute to REST
# controller index.php
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
RewriteRule . /index.php [L]
After building the images and running the containers, I've checked within the container's virtual filesystem again via CLI. The /etc/apache2/apache2.conf successfully holds the contents of my local file, and I've also done a apachectl -M within the container, and can see rewrite_module (shared) getting printed.
Again, I'm simply getting a 404; for example if I search for http://localhost/xyz. Same if I do not omit the port (and search for http://localhost:80/xyz). Searching for http://localhost and http://localhost:80 both work; so it seems that my rewrite rules are simply not being applied.
When running apachectl configtest within the docker container, I also get Syntax OK.
Just guessing; does this maybe have something with xdebugs outgoing communication from the container's port 9003?
What am I missing?
Apparently serverfault is the intended place for these questions; so I posted it over there (don't know how to migrate): https://serverfault.com/questions/1115336/cant-get-apache-rewrite-to-work-on-docker-php-apache-container
I've actually figured it out. The problem seems to be that, when using docker, you must specify the rewrite rules within the Virtual Host context of the port you've specified. At least I've done what's written above, plus the following steps:
Within the container, I've migrated (in CLI) to the folder etc/apache2/sites-enabled
In there is the file 000-default.conf, holding the default configs for the sites that are currently enabled on the concerned apache unit. As far as this application is concerned, it's thus fine to modify the default configuration, as the container will exclusively be used for this API. At least that was my thought.
So again I did the same; copy the file contents as they're shipped with an initiated docker container, via cat 000-default.conf.
Copy-paste the contents within the local apache/sites-enabled/000-default.conf file (that you newly create)
Now you take the following part that was previously in the local apache2.conf file (slightly improved):
# Rewrite for routing of all requests through REST controller
RewriteEngine On
RewriteRule ^index\.php$ - [L]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
RewriteRule ^(.*)$ /index.php [L]
and paste it into the local 000-default.conf file; before the closing </VirtualHost> tag. Then you remove the rewrite rules from your apache/apache2.conf file, but you leave it for the added ServerName directive.
You then add the path mapping to the volumes of your php container to load your custom virtual host default configuration file into your container, and you should be good to go; so your docker-compose.yml becomes:
services:
php:
....
volumes:
- ./php:/var/www/html
- ./php/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
- ./php/conf.d/error_reporting.ini:/usr/local/etc/php/conf.d/error_reporting.ini
- ./apache/apache2.conf:/etc/apache2/apache2.conf
- ./apache/sites-enabled/000-default.conf:/etc/apache2/sites-enabled/000-default.conf
....
Rewrite is now fully functional!
Related
I have to host 2 different web apps from a docker container. My first attempt at this worked, but I didn't like it. I have a lamp-base folder that has the Dockerfile that gets the server working, then a docker-compose.yml file that puts everything together. In my first version for the web apps I did build: ./lamp-base/ to make them build the web server, but I feel like that's the wrong approach as it leaves me with 3 images (one for each web app and one for the mysql server). I feel like this should be 2 images and 3 containers. 1 image for the mysql container and 1 image for the web server, then the web apps should be able to use the web server. I was able to build it and make it work, kind of.
The problem is, when I go to the URL I defined and added to my local /etc/hosts file I get the default welcome to apache page, I don't get my web apps. I honestly can't figure out why it isn't working correctly. The healthchecks pass, so the files are being hosted properly, but I can't access the web pages. This might be a little long, but I'll post up the Dockerfile and the docker-compose.yml file as well as the default.conf that get's added to the web server. I have asked all the people I know and no one can figure out why I can't reach the webpages, especially since the healthchecks pass. Thank you for bearing with me on this, here are the files...
Dockerfile...
FROM centos:7
EXPOSE 80/tcp
RUN yum install epel-release yum-utils -y && yum install https://rpms.remirepo.net/enterprise/remi-release-7.rpm -y
RUN yum-config-manager --enable remi-php74 && yum install php php-common php-opcache php-mcrypt php-cli php-gd php-curl php-mysql php-pdo php-pdo_mysql php-pecl-mcrypt php-sodium php-mbstring php-runtime php-json php-devel php-xml php-soap php-mysqlnd php-intl php-pecl-xlswriter httpd mod_ssl mod_php -y && yum clean all -y && rm /etc/httpd/conf.d/ssl.conf
ADD httpd.conf /etc/httpd/conf/httpd.conf
ADD conf.d/default.conf /etc/httpd/conf.d/default.conf
CMD /sbin/httpd -D FOREGROUND
The default.conf file that has the vhosts for the web apps...
<VirtualHost my.server1.com:80>
ServerName my.server1.com
CustomLog /var/log/httpd/access_log combinedio
ErrorLog /var/log/httpd/error_log
LogLevel notice
RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
RewriteRule .* - [F,L]
# Set application web root and directory settings
DocumentRoot "/var/www/server1/html"
<Directory "/var/www/server1/html">
Options -Indexes
AllowOverride none
php_value include_path ".:/var/www/server1/class"
php_value arg_separator.output ";"
php_value arg_separator.input ";"
</Directory>
</VirtualHost>
<VirtualHost my.server2.com:80>
ServerName my.server2.com
CustomLog /var/log/httpd/access_log combinedio
ErrorLog /var/log/httpd/error_log
LogLevel notice
RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
RewriteRule .* - [F,L]
# Set application web root and directory settings
DocumentRoot "/var/www/server2/html"
<Directory "/var/www/server2/html">
Options -Indexes
AllowOverride none
php_value include_path ".:/var/www/server2/class"
php_value arg_separator.output ";"
php_value arg_separator.input ";"
</Directory>
</VirtualHost>
I renamed the servers because I don't think I can post the actual server names, sorry.
The docker-compose.yml file...
version: "3.7"
networks:
lamp-net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
services:
lamp-web:
build: ./lamp-base/
image: lamp-web:dev
networks:
lamp-net:
ipv4_address: 172.20.0.6
ports:
- "80:80"
lamp-mysql:
image: mariadb:10.4
container_name: lamp-mysql
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
volumes:
- ./sql-files:/docker-entrypoint-initdb.d
networks:
lamp-net:
ipv4_address: 172.20.0.2
ports:
- "3307:3306"
healthcheck:
test: "/usr/bin/mysql --user=root --password=password --execute \"SHOW DATABASES;\""
interval: 3s
timeout: 2s
retries: 10
server-one:
image: lamp-web:dev
container_name: server-one
hostname: my.server1.com
depends_on:
lamp-mysql:
condition: service_healthy
volumes:
- ./server1:/var/www/server1
- ./Server1Config.php:/var/www/server1/class/Config.php
networks:
lamp-net:
ipv4_address: 172.20.0.3
healthcheck:
test: curl -kf http://server-one/home/landing/ || exit 1
interval: 3s
timeout: 2s
retries: 10
server-two:
image: lamp-web:dev
container_name: server-two
hostname: my.server2.com
volumes:
- ./server2:/var/www/server2
- ./Server2Config.php:/var/www/server2/class/Config.php
depends_on:
lamp-mysql:
condition: service_healthy
cr-web:
condition: service_healthy
networks:
lamp-net:
ipv4_address: 172.20.0.4
healthcheck:
test: curl -kf http://server-one/api/mp/inventory || exit 1
interval: 3s
timeout: 3s
retries: 10
It all builds correctly, I end up with 2 Images (mysql and server) and 3 containers (mysql, server-one, and server-two) all the healthchecks pass properly, but if I go to http://my.server1.com in my browser I only get the default apache welcome page, I don't get my actual server.
I have also tried removing the server from the docker-compose file and just doing docker build --no-cache . -t lamp-web:dev in the /lamp-base directory, then running the docker-compose up -d and again, it all builds properly I get the right containers, the healthchecks pass, but going to the URL in any browser just yields the default welcome to apache page. I can't figure out why the web containers aren't working correctly.
On a side note, if I remove the web server entirely and change the image: lamp-web:dev to build: ./lamp-base/ on both web containers it works correctly, but I end up with a bunch of images. Am I just going about this the wrong way? Should both the web containers have to build their own web server image? Any help is greatly appreciated. Thank you and sorry for the long post.
I am completely new to docker, php and apache, I am trying to run my sample website on apache server in a docker container. It works fine. And my dockerfile is following:
From php:7.2-apache
Copy Site/ /var/www/html/
Run echo "ServerName localhost" >> /etc/apache2/apache2.conf
RUN docker-php-ext-install pdo pdo_mysql
Expose 80
How can I have another website on the same apache sever in the docker container, for example "website1", in xampp I could change the httpd.conf file and add virtual host. but what should I do in docker container?
Update
============================================
I update the files as the following, This is docker-compose.yml
version: '3.9'
services:
apache:
build: .
container_name: php_cont
volumes:
- './apache2.conf:/etc/apache2/apache2.conf'
- './Site1/site1.conf:/etc/apache2/sites-available/site1.conf'
- './Site2/site2.conf:/etc/apache2/sites-available/site2.conf'
ports:
- '80:80'
This is the site.conf
<VirtualHost *:80>
DocumentRoot "d:\Temp\Site1"
ServerName site1Name
<Directory "d:\Temp\Site1">
</Directory>
</VirtualHost>
This is the site2.conf
<VirtualHost *:80>
DocumentRoot "d:\Temp\Site2"
ServerName site2Name
<Directory "d:\Temp\Site2">
</Directory>
</VirtualHost>
and I addded these two line to hosts file in windows=>system32=>driver=>etc
127.0.0.1 site1Name
127.0.0.1 site2Name
but when I surf to site1Name or site2Name on the browser, there is no success!
The idea of Docker is that every application (website in you case) has its own environment. You can then upgrade the PHP version for one application without affecting the others. So you should have a Dockerfile per website.
To answer your question “how to direct visitors to the correct container?”: please take a look into Reverse Proxies. There are plenty of Docker images available like Nginx and Traefik. These can also take care of SSL certificates for instance.
I recommend you using Docker Compose, but you also can use Volume in both docker run command and docker-compose.
docker-compose
First of all let's see docker-compose. Create a file called docker-compose.yml with the following contents:
version: '3.9'
services:
apache:
build: .
hostname: OPTIONAL
container_name: OPTIONAL
volumes:
- './apache/apache2.conf:/etc/apache2/apache2.conf'
- './apache/site1.conf:/etc/apache2/sites-available/site1.conf'
ports:
- '80:80'
The two keywords hostname and container_name are optional and you can change their values or even remove these two lines.
Regarding volumes, copy your apache2.conf to a path like ./apache and do your edits in this file.
Then add another file called site1.conf. Here is my conf file as an example:
<VirtualHost *:80>
ServerName some_name.com
ServerAdmin webmaster#name
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
For each site you can create another file and add it to volumes.
Now after everything is done, run this command:
docker-compose up -d
docker run command
If you want to use docker run command, you can do like this (the above paths in ./apache in the below are the same):
docker run IMAGE_NAME -v './apache/apache2.conf:/etc/apache2/apache2.conf' -v './apache/site1.conf:/etc/apache2/sites-available/site1.conf' -dp80:80
Update 1
Edit your Dockerfile:
From php:7.2-apache
Copy Site/ /var/www/html/
Run echo "ServerName localhost" >> /etc/apache2/apache2.conf
COPY site1.conf /etc/apache2/apache2.conf
COPY site2.conf /etc/apache2/apache2.conf
COPY apache2.sh /root/
RUN bash /root/apache2.sh
RUN rm /root/apache2.sh
RUN docker-php-ext-install pdo pdo_mysql
Expose 80
apache2.sh:
service apache2 start
a2ensite site1
a2ensite site2
service apache2 restart
I'm currently configuring my raspberry pi as my personal home server and I want to use docker. I have few different containers :
-Apache/PHP 81:80
-Nextcloud 8080:80
-Minecraft 25565:25565
I already have a dynamic domaim (http://felixbestwaifu.hopto.org)
When I try to access felixbestwaifu.hopto.org:8080, it timeouts :c
My goal
Nextcloud
cloud.hostname.org
or hostname.org/nextcloud
Website
Domain.org
Domain.org/about.php
Domain.org/Anime.php
I tried using NGINX by creating a nextcloud.conf in /sites-enabled/ and /sites-available/, but it didn't work at all ^^'
server {
listen 80;
server_name nextcloud;
location /nextcloud {
proxy_pass 192.168.2.150:8080/;
}
}
Note : 192.168.2.150 is my pi static IP in my network
So here is my question : How do you set-up a reverse proxy for docker containers ?
I would really appreciate your help >//< (sorry for bad English)
Let us suppose your Apache-PHP container is called apache_php. You should have two .conf files there with similar contents (less or more is regarding what you need in your conf files).
/etc/apache2/sites-available/minecraft.conf:
<VirtualHost *:80>
ServerName minecraft.com
ServerAdmin webmaster#minecraft
DocumentRoot /home/minecraft
ProxyPreserveHost On
ProxyPass "/" "http://minecraft:25565/"
ProxyPassReverse "/" "http://minecraft:25565/"
ProxyRequests Off
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
/etc/apache2/sites-available/nextcloud.conf:
<VirtualHost *:80>
ServerName nextcloud.com
ServerAdmin webmaster#nextcloud
DocumentRoot /home/nextcloud
ProxyPreserveHost On
ProxyPass "/" "http://nextcloud:8080/"
ProxyPassReverse "/" "http://nextcloud:8080/"
ProxyRequests Off
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Note the trailing / in ProxyPass and ProxyPassReverse. If you do not add them, final URLs will be something like http://minecraft:25565your_page, while with trailing / it will be http://minecraft:25565/your_page.
In this case if anyone enters minecraft.com, your Apache proxies him to minecraft site and *minecraft is only accessible by your Apache container.
Note that minecraft.com and minecraft are two different websites in a network called proxy that I talk about it below
This is what I do in my container, but let me explain it up to my knowledge:
Suppose Your Apache, your Nextcloud and Minecraft are in the same network (in this case you should create a network, in my case it's called proxy network) and all of these three containers should see each other by pinging.
How your docker-compose.yml file should look like in this case:
version: '3.9'
services:
apache_php:
build: .
restart: always
ports:
- "81:80"
minecraft:
build: .
restart: always
ports:
- "25565:25565"
nextcloud:
build: .
restart: always
ports:
- "8080:80"
networks:
default:
external:
name: proxy
volumes:
db_data: {}
So in this file, I'm creating three images called apache_php, minecraft and nextcloud that is built via Dockerfile via build keyword. I also proxy ports 81 to 80, port 25565 to 25565 and port 8080 to 80.
Then you can create a Dockerfile or modify existing one with similar contents:
FROM your_os_base:tag
COPY minecraft.conf /etc/apache2/sites-available/
COPY nextcloud.conf /etc/apache2/sites-available/
COPY my_bash_commands.sh /root/
RUN chmod +x /root/my_bash_commands.sh
RUN /root/my_bash_commands.sh
ENTRYPOINT ["your_thing"]
Now what is my_bash_commands.sh in my case? As default shell is sh in most cases, I create a bash file so that I get my desired results.
The my_bash_commands.sh is similar to this:
#!/usr/bash
service apache2 start
a2ensite minecraft
a2ensite nextcloud
a2enmod proxy
a2enmod proxy_http
service apache2 restart
Now you can create them by running docker-compose up -d if you are in the path where docker-compose.yml is, else you should use docker-compose -f /absolute_patht_to/docker-compose.yml up -d.
Summary: 2 separate applications, both using docker-compose, how can I have http://app-1.test and http://app-2.test available at the same time?
Description:
I feel like I've missed something super-simple. I have 2 php-fpm (via nginx) applications, both run by similar docker-compose setups, somewhat like:
# docker-compose.yaml
version: '3'
services:
app:
build:
context: .
dockerfile: docker/Dockerfile
container_name: app_1
tty: true
depends_on:
- db
- dbtest
working_dir: /var/www
volumes:
- ./:/var/www
webserver:
image: nginx:stable
container_name: app_1_webserver
restart: always
ports:
- "80:80"
depends_on:
- app
volumes:
- ./:/var/www
- ./docker/app.conf:/etc/nginx/conf.d/default.conf
links:
- app
# ...
On my /etc/hosts, I can add something like
127.0.0.1 app-1.test
Now I can call the app via the browser by going to app-1.test.
The second one has a similar setup, but of course it won't go up, because port 80 is blocked. I can of course change the port, but then the url would be something like app-2.test:81 instead of app-2.test. What can I do, so I can run a second application under a different local hostname? Or is using a different port the best way to go?
You can't. What you can do is add a "router" in front of your images (a third image) which does routing (proxy passing) based on the host name.
Apache or Nginx are often used for these kinds of things.
e.g. with apache server
https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
<VirtualHost *:80>
ServerName app-1.test
ProxyRequests Off
ProxyPreserveHost On
ProxyPass / http://image1:80/
ProxyPassReverse / http://image1:80/
ErrorLog /var/log/apache2/error.log
LogLevel info
CustomLog /var/log/apache2/access.log combined
</VirtualHost>
<VirtualHost *:80>
ServerName app-2.test
ProxyRequests Off
ProxyPreserveHost On
ProxyPass / http://image2:80/
ProxyPassReverse / http://image2:80/
ErrorLog /var/log/apache2/error.log
LogLevel info
CustomLog /var/log/apache2/access.log combined
</VirtualHost>
now you can add both names on the same ip in your /etc/hosts file and the server can route internally based on the provided hostname (ServerName).
The http://image1:80/ (and its like) references should be changed to the docker internal dns like specified in the docker-compose.yml
I am trying to setup an NGINX server and mount a directory to the container. I have a DigitalOcean server running, and want to link my website data into the nginx container.
Part of the docker file is:
webserver:
depends_on:
- wordpress
image: nginx:latest
container_name: webserver
restart: unless-stopped
# Expose port 80 to enable the config options defined nginx.conf
ports:
- "80:80"
- "443:443"
# combiation of named volumes and bind mounts
# bind wordpress app code
# bind nginx config dir on host
# mount certbot certificates and keys for domain
volumes:
- wp_data:/var/www/html
- ~/custom:/etc/nginx/conf.d/custom
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- kgNetwork
command: /bin/bash -c "envsubst < /etc/nginx/conf.d/custom > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"
nginx.conf
server {
# Tells ngxinx to listen on port 80
listen 80;
listen [::]:80;
root /usr/share/nginx/html;
index index.html index.htm;
location /
{
try_files $uri $uri/ =404;
}
}
the container starts but I don't see my linked test html file. the log shows: envsubst: error while reading "standard input": Is a directory
I am not quite sure how to understand this. On my server I created in my home folder the subfolder "custom" containing an index.html.
My thought process so far was:
Create a custom html in the host folder
Mount the volume via ~/custom:/etc/nginx/conf.d/custom
Run the command: /bin/bash -c "envsubst < /etc/nginx/conf.d/custom > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"to setup the custom website on nginx.
The nginx container is running but loading the site does not show anything. I am new to docker and tried to debug for 2hrs now but I am clearly missing the point :)
Thanks
Sebastian
You say you created $HOME/custom/index.html. When you launch the container, you do it with a bind mount ~/custom:/etc/nginx/conf.d/custom; that mounts the directory $HOME/custom into the nginx configuration directory. When you then try to run envsubst, its input is the custom directory, which leads to the error you get.
If that directory actually contains the HTML files, you need to mount it on the location you specified in your configuration
volumes:
- ~/custom:/usr/share/nginx/html
If you're trying to do environment-variable substitution on the HTML content at deploy time, you also need to change the path in the envsubst command. Consider using a Docker entrypoint wrapper script to do this templating. Also remember that writes into the mounted directory after container startup are bidirectional, so with this setup you might have trouble running multiple containers off the same (shared) host content.