====Docker====
Docker is similar to [[:tools:lxc|LXC]], however it is geared to running a single
application within its jailed system rather than being a lightweight
VM. It's like having a chroot management system enhanced with networking. One side
effect of not having an init process is zombie processes can accumulate in the container. Docker is also dependant on the overlay2 file system which is relatively slow.
Docker has a lot of pre-built applications, and due to this the docker ecosystem
is a security nightmare and cannot really be recommended unless you
skip this and create your own builds from base O/S images such as Alpine Linux.
On this page are demos of using Docker on Linux. One container
is an install of Laravel and another is Alpine Linux. The installation
of Laravel should demonstrate the security issues when using pre-built docker
images.
Alpine Linux is further configured for PHP8, composer and Symfony. This
is not for production, just for development and not fully configured here, yet.
Later a build is investigated containing WordPress using Alpine Linux
and accessed via a proxy with nginx.
===Environment===
VirtualBox virtual machine manager or Xen virtual machine.
===Operating System===
Devuan Chimaera on VirtualBox or Debian Bullseye on Xen.
===Docker===
==Install==
apt-get install apparmor
apt-get install docker.io
apt-get install docker-compose
==User==
Add your login user to the docker group with vigr and vigr -s and relog.
==Run==
/etc/init.d/docker start
===Laravel===
wget "https://laravel.build/example-app" -O example-app.sh
sh ./example-app.sh
cd example-app
./vendor/bin/sail up
===Alpine Linux===
Alpine Linux is a lightweight Linux ideal for running inside a container.
==Install==
Fetch the image and create the container.
docker pull alpine:latest
docker create -t -i --name alpine_linux alpine:latest
==Start==
Start Alpine Linux.
docker start alpine_linux
==Shell==
Connect to a shell within Alpine Linux.
docker exec -it alpine_linux /bin/sh
==Stop==
Stop Alpine Linux
docker stop alpine_linux
==Delete==
How do delete Alpine Linux if necessary.
docker rm alpine_linux
===PHP8===
==Install==
docker exec -it alpine_linux /bin/sh
apk update
apk upgrade
apk add php8 php8-fpm php8-opcache php8-cli php8-ctype php8-iconv php8-session php8-simplexml php8-tokenizer php8-openssl php8-phar
ln -sf /usr/bin/php8 /usr/local/bin/php
exit
===Composer===
==Git==
docker exec -it alpine_linux apk add git
==Install==
Visit the composer downloads page to find the composer installer script.
[[https://getcomposer.org/download/?lang=php|Composer Downloads]]
Save the script as composer-installer.sh
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
Run the saved script.
sh composer-installer.sh
Installer verified
All settings correct for using Composer
Downloading...
Composer (version 2.3.5) successfully installed to: /root/composer.phar
Use it: php composer.phar
Move the blob into your path.
mv composer.phar /usr/local/bin/
===Symfony===
==Install==
Fetch the symfony blob.
wget "https://github.com/symfony-cli/symfony-cli/releases/download/v5.4.8/symfony-cli_linux_amd64.tar.gz"
tar zxvf symfony-cli_linux_amd64.tar.gz
Move the blob into your path.
mv symfony /usr/local/bin/
==Application==
Create a symfony application.
symfony new --webapp my_project
===Fixed IP address===
==Bridge==
Create a network bridge using docker.
docker network create --subnet=10.44.0.0/24 vlan
==Run==
Create and run the container. The option d starts the container in the background and t assigns a pseudo tty.
docker pull alpine:latest
docker run -d -t --net vlan --ip 10.44.0.10 --name alpine_linux alpine
==Command==
Run a command in the container
docker exec -it alpine_linux /sbin/ifconfig eth0
eth0 Link encap:Ethernet HWaddr XX:XX:XX:XX:XX:XX
inet addr:10.44.0.10 Bcast:10.44.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:10 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:876 (876.0 B) TX bytes:0 (0.0 B)
==Stop==
Stop container
docker stop alpine_linux
==Restart==
Restart container
docker start alpine_linux
===WordPress development===
This is the process I used to develop a WordPress docker container. Following this is
the method using a Dockerfile which automates the process once it is known. Some differences
are also found in the application of the container during development.
==Proxy==
Install nginx in the main host.
apt install nginx-full php php-cli php-fpm
Use proxy_pass to direct a https URL to the container.
Eg.
location / {
proxy_pass http://x.x.x.x:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_buffering off;
}
==Apache2==
Install apache2 in the container.
__Install__
docker exec -it alpine_linux sh
apk update
apk upgrade
apk add php8 php8-apache2 php8-mysqli php8-mysqlnd php8-opcache php8-cli php8-ctype php8-iconv php8-session php8-simplexml php8-tokenizer php8-openssl php8-phar php8-curl php8-dom php8-exif php8-fileinfo php8-pecl-imagick php8-mbstring php8-zip php8-gd php-intl
ln -sf /usr/bin/php8 /usr/local/bin/php
exit
__Config__
At the least edit the Listen and ServerName directives in /etc/apache2/httpd.conf
Listen *:80
ServerName WordPress
ServerAdmin root
ServerTokens Prod
ServerSignature Off
__Logging__
Use mod remoteip resolve client ip address.
__Newer apache2__
RemoteIPProxyProtocol On
RemoteIPProxyProtocolExceptions 127.0.0.1 X.X.X.X/24
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy X.X.X.X
__Older apache2__
RemoteIPHeader X-Real-IP
RemoteIPTrustedProxy X.X.X.X
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
__Run__
Start apache2 in the container.
docker exec -it alpine_linux /usr/sbin/httpd -DFOREGROUND
To keep this process up, start it from RUNIT or similar in the main docker host.
__run__
/usr/bin/docker start alpine_linux
exec /usr/bin/docker exec -e TZ=UTC -e PHP_INI_SCAN_DIR=/etc/php8/conf.d -t alpine_linux /usr/sbin/httpd -DFOREGROUND
__finish__
/usr/bin/docker stop alpine_linux
==WordPress==
Install WordPress in the container.
docker exec -it alpine_linux sh
cd /var/www/localhost/htdocs
wget "https://wordpress.org/latest.zip"
unzip latest.zip
rm latest.zip
chown -R apache:apache wordpress
exit
The URL will become https://example.com/wordpress/ and the setup URL will be
https://example.com/wordpress/wp-admin/setup-config.php
If a MySQL server is not available then MySQL must be started in another
container in the same network.
Add custom config values to support proxy.
define('FORCE_SSL_ADMIN', true);
// in some setups HTTP_X_FORWARDED_PROTO might contain
// a comma-separated list e.g. http,https
// so check for https existence
if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false)
$_SERVER['HTTPS']='on';
define('FS_METHOD','direct');
===WordPress Dockerfile===
A docker file can be used to automate the building of an image after one
has been developed.
Here is a simple Makefile just used to contain various rules.
clean:
rm -f *~
build:
docker build --no-cache -t alpine_wp .
run:
docker run -d -t --net vlan --ip 10.44.0.11 --name wp-dev alpine_wp
test:
curl -v http://10.44.0.11/
stop:
docker stop wp-dev
rm:
docker rm -f wp-dev
docker image rm -f alpine_wp
The Makefile uses a Dockerfile to make the build, as follows:
# LATEST WORDPRESS ON ALPINE LINUX
FROM alpine:latest
# INSTALL APACHE2/PHP8
RUN apk --no-cache update && apk --no-cache upgrade && apk --no-cache add php8 php8-apache2 php8-mysqli php8-mysqlnd php8-opcache php8-cli php8-ctype php8-iconv php8-session php8-simplexml php8-tokenizer php8-openssl php8-phar php8-curl php8-dom php8-exif php8-fileinfo php8-pecl-imagick php8-mbstring php8-zip php8-gd php-intl && ln -sf /usr/bin/php8 /usr/local/bin/php
# INSTALL WORDPRESS
WORKDIR /var/www/localhost/htdocs
RUN wget -q "https://wordpress.org/latest.zip" && unzip -q latest.zip && rm latest.zip && chown -R apache:apache wordpress && rm -f index.html && echo "" > index.php
# START HTTPD
EXPOSE 80
ENTRYPOINT /usr/sbin/httpd -DFOREGROUND
===Miscellaneous===
==Copy file==
docker cp .vimrc alpine_linux:/root/.vimrc
==Apache log rotation==
#! /bin/sh
# Apache log rotation.
#
# Dmb May 1999 - Jun 2022.
# Alpine Linux:
#
# apk add apache2-utils webalizer
#
# 0 1 * * * /usr/bin/docker exec alpine_linux /root/rotatelog 1>/dev/null 2>/dev/null
umask 022
LOGDIR="/var/log/apache2"
WEBLOG="access.log"
LOGFILES="$WEBLOG error.log"
STATDIR="stats"
cd $LOGDIR
for f in $LOGFILES
do
if test -f $f; then
test -f $f.6.gz && mv $f.6.gz $f.7.gz
test -f $f.5.gz && mv $f.5.gz $f.6.gz
test -f $f.4.gz && mv $f.4.gz $f.5.gz
test -f $f.3.gz && mv $f.3.gz $f.4.gz
test -f $f.2.gz && mv $f.2.gz $f.3.gz
test -f $f.1.gz && mv $f.1.gz $f.2.gz
test -f $f.0 && mv $f.0 $f.1 && gzip $f.1
cp -p $f $f.0
cat /dev/null >$f
fi
done
if test -f $WEBLOG.0; then
TMPLOG=`mktemp /tmp/logresolve.XXXXXXXXXX`
logresolve < $WEBLOG.0 > $TMPLOG
mkdir -p $STATDIR
webalizer -Q -p -n"localhost" -o$STATDIR $TMPLOG
rm -f $TMPLOG
fi
exit 0
==Clone==
__First stop the container to commit any changes__
The commit creates a tagged version of the container which
has any changes made since installation.
docker stop alpine_lunux
docker commit alpine_linux wordpress:v1
__Container can be restarted now__
docker start alpine_linux
__Save the docker container as an image__
This saves the container at the specified commit tag for import
elsewhere.
docker save -o wordpress_v1.tar wordpress:v1
Container image can now be used elsewhere.
===Resources===
[[https://docs.docker.com/get-started/|Docker HOWTO]]
[[https://docs.docker.com/develop/develop-images/dockerfile_best-practices/|Dockerfile Tips]]