DockerFile: WordPress

Let’s take a look at Docker‘izing WordPress.

 

The Docker pull command is:

docker pull wordpress

pull: pulls an image or a repository from a registry. It doesn’t run it. It just means you have the image locally.

https://docs.docker.com/engine/reference/commandline/pull/

 

You can actually run the image using:

docker run --name some-wordpress --link some-mysql:mysql -d wordpress

some-wordpress is going to be the name of the container.

--link is a bit old school. It connects one container to another. i.e. MySQL to WordPress. Nowadays we use user-defined networks – e.g. overlays. https://docs.docker.com/network/links/

WordPress Docker Repo: https://hub.docker.com/_/wordpress/

 

However, before you can run it you’ll need MySQL. So:

docker pull mysql:5.7.24

(Aside: why not use mysql or mysql:latest? ‘cos MySQL 8 changed the password authentication method. See below.)

and then run it with:

docker run --name test-mysql -e MYSQL_ROOT_PASSWORD=test -d mysql:5.7.24

--name is the name you’re giving to the container,

MYSQL_ROOT_PASSWORDis an environment variable that you set which is read in the container. Note: with MySQL this is done programmatically via docker-entrypoint.sh – https://github.com/docker-library/mysql/blob/696fc899126ae00771b5d87bdadae836e704ae7d/8.0/docker-entrypoint.sh . For more on ARG, Environment variables: https://stackoverflow.com/questions/53592879/dockerfile-and-environment-variable/53593826#53593826

and -d means detach.

To specify a tagged version just add it after the image using a colon. E.g. mysql:5.7.24.

 

Once you’ve run it you can test it by exec‘ing in with

docker exec -it test-mysql bash

and running:

mysql -u root -p test

or check logs with:

docker logs test-mysql

E.g.

`2018-12-03T14:24:57.091306Z 0 [Note] mysqld: ready for connections.
Version: ‘5.7.24’ socket: ‘/var/run/mysqld/mysqld.sock’ port: 3306 MySQL Community Server (GPL)`

 

Or even mysql in via another MySQL container using:

docker run -it --link test-mysql:mysql --rm mysql sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"'

https://hub.docker.com/_/mysql/

 

So, getting back to WordPress let’s run it now with:

docker run --name test-wordpress --link test-mysql:mysql -d wordpress

and check the logs with:

docker logs test-wordpress
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
[Mon Dec 03 14:26:29.086884 2018] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Debian) PHP/7.2.12 configured -- resuming normal operations
[Mon Dec 03 14:26:29.086951 2018] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'

You should then be able to access your WordPress site on http://localhost:8080

 

Errors

Conflict. The container name “/test-wordpress” is already in use by container

You run

docker run --name test-wordpress --link test-mysql:mysql -d wordpress

and get:

docker: Error response from daemon: Conflict. The container name "/test-wordpress" is already in use by container "0ea70abdaf306d896eb71f3ab585961359f27af23a243b81370bf407d3dd846d". You have to remove (or rename) that container to be able to reuse that name.

You’ve already got a container with that name.

Remove it with: `docker rm test-wordpress`

https://stackoverflow.com/questions/31676155/docker-error-response-from-daemon-conflict-already-in-use-by-container

This might happen if the container exited and you try and relaunch it.

 

Site can’t be reached

You plug http://localhost:8080 into the web browser but get:

This site can’t be reached localhost refused to connect.
Did you mean http://localhost-8080.com/?
Search Google for localhost 8080
ERR_CONNECTION_REFUSED

Checking the WordPress logs with `docker logs test-wordpress` I can see:

Warning: mysqli::__construct(): The server requested authentication method unknown to the client [caching_sha2_password] in Standard input code on line 22

Warning: mysqli::__construct(): (HY000/2054): The server requested authentication method unknown to the client in Standard input code on line 22

MySQL Connection Error: (2054) The server requested authentication method unknown to the client

however this is a secondary problem. Why are we getting this?

‘cos MySQL 8 introduced a different type of authentication – https://github.com/docker-library/wordpress/issues/313

If you are getting this then you need to use: `docker pull mysql:5.7.24` or use a different auth method.

 

Back to the problem at hand – we should still be able to see a (non-functioning) WordPress site on that port. i.e. Apache should be running.

Let’s just do a sanity check:

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
05abd11d7830        wordpress           "docker-entrypoint.s…"   23 seconds ago      Up 22 seconds       0.0.0.0:8080->80/tcp   test-wordpress

This `0.0.0.0:8080->80/tcp` means the docker host port 8080 is mapped to the container port 80.

https://stackoverflow.com/questions/41798284/understanding-docker-port-mappings

so http://localhost:8080 is correct.

 

It seems the problem really was the lack of MySQL. Using 5.7.24 and looking at the WordPress logs showed (for a successful installation):

docker logs test-wordpress
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
[Mon Dec 03 14:26:29.086884 2018] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Debian) PHP/7.2.12 configured -- resuming normal operations
[Mon Dec 03 14:26:29.086951 2018] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'

 

Installing MySQL and WordPress in under a minute:

https://asciinema.org/a/nW3l0A1hi6wzMde4RUQ1QVPsc?speed=3

 

 

 

 

 

Containerizing an App – the Dockerfile

Containerizing an App

Notes:

  • CAPITALIZE instructions
  • <INSTRUCTION> <value>
  • FROM always first instruction
  • FROM = base image
  • Good practice to list maintainer
  • RUN = execute command and create new layer
  • COPY = copy code into image as new layer
  • Some instructions add metadata instead of layers

 

Dockerfile

FROM <base image>

LABEL maintainer=”<eg_your@email.com>”

RUN apk –update nodejs nodejs-npm

COPY . /src

WORKDIR /src

RUN npm install

EXPOSE 8080

ENTRYPOINT [“node”, “./app.js”]

Build with:

docker image build -t mywebapp .

or docker build

docker container run -d --name web1 -p 8080:8080 mywebapp

d=> detached

-p host port:container port

 

Under the hood

Dockerfile just some text instructions for building images.

FROM => creates a layer

LABEL => create metadata

RUN => exec commands

COPY => copy stuff in and create a new layer

WORKDIR => working directory

 

Build context => location of your code.

Subfolders gets included too.

 

Note: the docker client can be on a separate machine from the docker daemon. The context just gets sent. Also, the context could also be a git repo. E.g.

docker image build -t mywebapp https://<github-url>

Multi-stage Builds

Stage 0:

FROM node:latest AS storefront

Stage 1:

FROM maven:latest AS appserver

Stage 2:

FROM java:8-jdk-alpine AS production

COPY --from=storefront /usr/src/atsea/app/react-app/build/ .

This last instruction is key. It pulls out the layer with the build code we need from that image.

E.g. if we build with:

docker image build -t multistage .

we get:

docker image ls
REPOSITORY                TAG                 IMAGE ID            CREATED             SIZE
multistage                latest              981a05b4af34        2 minutes ago       210MB
<none>                    <none>              8f1ec3e20ba0        3 minutes ago       775MB
<none>                    <none>              a631e23c40a5        9 minutes ago       832MB
node                      latest              b064644cf368        2 weeks ago         673MB
maven                     latest              1361ae58b0a4        2 weeks ago         635MB
java                      8-jdk-alpine        3fd9dd82815c        18 months ago       145MB

Here you can see how the multistageimage is a fraction of its build stage images.