The last time I did any WordPress development was over a year ago so I no longer have PHP and MySQL installed. I started marching off down my well beaten path. Download PHP and MySQL, install on my machine, do dev work. Just after clicking on the MySQL download I suddenly thought, what the hell am I doing? DOCKER!!!! I already have Hyper-V installed so all I need is a container and some YUMMY YAML.
I’ve used Docker once before, for around 5 minutes, so I’m a complete noob really. So going through this process to work it all out for myself was initially extremely painful. I failed on my first session and ended up having to walk away from it all as I just wasn’t getting it.
I did a lot of reading and Googling and just wasn’t finding the explanation I needed to grok it all properly. There are lots of explanations of how to get this working but they all seemed to stop at the crucial point for me. They covered off some yml to get MySQL and WordPress containers up and running but stopped there. What about persistence? Or deployment? Where the hell are all the WordPress files?
Some of them seemed to demo the solution I wanted but seemed to miss out on how they achieved it, or were doing it on different platforms. I needed a noobs Docker for Dummies walk through. So I’m going to document what I’ve found out in the hope that it crosses some of the Ts and dots the Is for others getting started.
Docker is The New VM
Don’t get me wrong virtual machines are great and very necessary but they’re also a bit overkill a lot of the time. This is where Docker comes in. It still requires virtualisation technology under the hood but it’s now transparent and not directly controlled.
Microsofts own virtualisation technology is Hyper-V. Docker on Windows uses this by default but it could be used with VirtualBox from Oracle as well. I’ve had lots of success running virtualised OSes on top of Hyper-V and more or less utter failure using it for Microsofts own emulators, the irony here isn’t lost on me by the way.
Docker is a container technology that wraps up specific services, such as databases, runtime or any other dependencies tasks require. It lets you run just what you need for a given task without installing these services on the host OS. Fantastic. Lets dig in.
Dead easy. Make sure Hyper-V is installed on your Windows box (Home users you’re out of luck here btw). Go here, download for your OS and architecture, install. Done.
The Docker installation is a very painless process.
Once installed., open a command line (Windows + X -> Command Prompt (Admin)) and execute:
You should then see some version information:
Client: Version: 17.06.0-ce API version: 1.30 Go version: go1.8.3 Git commit: 02c1d87 Built: Fri Jun 23 21:30:30 2017 OS/Arch: windows/amd64 Server: Version: 17.06.0-ce API version: 1.30 (minimum version 1.12) Go version: go1.8.3 Git commit: 02c1d87 Built: Fri Jun 23 21:51:55 2017 OS/Arch: linux/amd64 Experimental: true
If you see an error mentioning the daemon, Docker may well still be setting itself up in the background. You can also create a Docker ID on the Docker site and configure your install to use this ID though I’ve not needed to so far so cannot comments on this aspect.
Next run this:
This gives you some useful info about your Docker environment, like so:
C:\WINDOWS\system32>docker info Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 1 Server Version: 17.06.0-ce Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs Plugins: Volume: local Network: bridge host ipvlan macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog Swarm: inactive Runtimes: runc Default Runtime: runc Init Binary: docker-init containerd version: cfb82a876ecc11b5ca0977d1733adbe58599088a runc version: 2d41c047c83e09a6d61d464906feb2a2f3c52aa4 init version: 949e6fa Security Options: seccomp Profile: default Kernel Version: 4.9.36-moby Operating System: Alpine Linux v3.5 OSType: linux Architecture: x86_64 CPUs: 2 Total Memory: 3.837GiB Name: moby ID: 3RBI:664X:UXGI:FB6Y:3K7K:LEMA:BWRR:6SLX:5M7J:P66D:T4XN:L7XH Docker Root Dir: /var/lib/docker Debug Mode (client): false Debug Mode (server): true File Descriptors: 15 Goroutines: 25 System Time: 2017-08-06T15:06:13.8983022Z EventsListeners: 0 Registry: https://index.docker.io/v1/ Experimental: true Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false
It’s probably worth having a look over your Docker settings before doing anything else. You can right click on the Task Tray Docker icon to see various options, Click on Settings first and have mooch about to see if things are setup as you’d like.
The General tab is pretty self-explanatory and controls how Docker starts up and some security settings. You can probably leave most of these alone.
The Shared Drives page is also pretty self explanatory. Using this page you can control which host machine drives are available to services running within containers. Since I’m using my H drive for my projects I’ve shared this drive with Docker. This allows processes running inside your Docker containers to access stuff on the host machine drives.
The Advanced tab is worth reviewing. If you have used Hyper-V before and have customised it’s setup you’ll find that Docker automatically picks up some of these settings. I’d configured Hyper-V to put VM images on one of my large external drives so Docker will install it’s Moby Linux VM here.
I’ve also upped the available RAM Docker can use to 4Gb, my dev box has 24Gb so I’ve plenty to throw at Docker. Since it’s running on Linux 4Gb RAM should be more than enough to keep things running at a decent speed.
The Network, Proxies and Daemon pages are fine in their default state for now.
I wanted a WordPress development environment with persisted data. If Docker is restarted or the host machine reboots, I want the state of both stored. Both in terms of the WordPress state such as themes, plugins and so on and the database. I’m fast with WordPress development but not that fast!
So Docker should accept all the responsibility of hosting Apache, PHP, MySQL and phpMyAdmin. Docker also takes care of all the networking and configuration of those services. The host Windows machine exposes a drive and hosts the WordPress files and MySQL databases on it’s “normal” file system. These files are stored in directories that are mapped into the containers which allows for simple deployment once the development is complete.
I’m sure in time and as I learn more about Docker I’ll find a lot of this can be handled better. For now this is where I am in the learning curve and it’s working.
Yummy YAML & Docker Compose
The idea here is to produce a file that tells Docker what you want to do. It specifies one or more images to use to create one or more containers that provide the services you need. Images are templates for containers, much in the same way classes can be thought of as templates for objects in an OO sense. There are some issues here in terms of terminology which I don’t get. Although we are creating containers, in the .yml files they are called services. It’s probably my limited knowledge here but it would be clearer to new users if they just stuck to using the same terms.
What I was struggling to understand and configure was volumes. I’m still a little in the dark to be honest and I’m not entirely sure I have this configured in the best way. But what I’m showing here is working and it suits the requirements I mentioned above.
As I showed above I’ve shared the host machines H drive. Within this drive I have the following directory structure:
H:\js2017 H:\js2017\database H:\js2017\ui
In the root directory (H:\js2017) I have created a Docker compose YML file called docker-compose.yml. This is where the magic happens. The file contains this YAML:
version: '2' services: wordpress: depends_on: - db image: wordpress restart: always volumes: - ./ui:/var/www/html environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_PASSWORD: p4ssw0rd! ports: - 8082:80 networks: - back db: image: mysql restart: always volumes: - ./database:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: p4ssw0rd! networks: - back phpmyadmin: depends_on: - db image: phpmyadmin/phpmyadmin restart: always ports: - 8083:80 environment: PMA_HOST: db MYSQL_ROOT_PASSWORD: p4ssw0rd! networks: - back networks: back:
So in this file we have 3 services defined. We have wordpress, db, phpmyadmin. We also have a network aliased back (backend). You could almost certainly take this file, alter the ports if need be and the volume entries and have this up and running pretty quickly.
You can see the references to the database and ui directories in the volumes declaration for the wordpress and db services. The “.” notation is the relative path from the docker-compose.yml file in the project root directory. These are mapped by docker to the containers internal file system running on top of Linux within the Docker virtual machine. So anything written to the /var/www/html directory within the container ends up in the ui directory on the host machine and the same for the /var/lib/mysql directory for the databases.
The port mappings for the services are mapping host machine ports to container ports (hostmachineport:containerport). So to view the WordPress site, I navigate to localhost:8082 in my host machines browser and this is forwarded to port 80 in the container and serves the page.
Going over the deployment of the MySQL database to a live server is beyond the scope of this article but to deploy the WordPress site it’s just a case of taking the contents of the ui directory and uploading it to the public html directory of your web server.
I’m sure there is a better way of managing this but for now with my limited understanding of the finer details of Docker and it’s container model this works for me. Hopefully this has gotten you started and I’m sure I’ll revisit this again in the not too distant future with some better solutions and tips.
Also published on Medium.