Categories
Linux

Install Docker CE on RHEL8

As per Red Hat documentation Docker is not supported in RHEL 8.

The Podman, Skopeo, and Buildah tools were developed to replace Docker command features. Each tool in this scenario is more lightweight and focused on a subset of features.

For my latest work project, however, where we will be deploying Kubernetes clusters with Rancher we need RHEL8 and Docker.

Rancher Support Matrix

Manual Install

Following https://linuxconfig.org/how-to-install-docker-in-rhel-8

Add and enable the docker-ce repo with dnf config-manager. Verify with repolist:

$ sudo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo

[ec2-user@ip-192-169-2-20 ~]$ sudo dnf repolist -v  | grep docker-ce-stable -A10
repo: using cache for: docker-ce-stable
docker-ce-stable: using metadata from Wed 02 Jun 2021 07:27:37 PM UTC.
....

Repo-id            : docker-ce-stable
Repo-name          : Docker CE Stable - x86_64
Repo-revision      : 1622662057
Repo-updated       : Wed 02 Jun 2021 07:27:37 PM UTC
Repo-pkgs          : 38
Repo-available-pkgs: 38
Repo-size          : 937 M
Repo-baseurl       : https://download.docker.com/linux/centos/8/x86_64/stable
Repo-expire        : 172,800 second(s) (last: Tue 15 Jun 2021 03:49:08 PM UTC)
Repo-filename      : /etc/yum.repos.d/docker-ce.repo

Display available versions and install with dnf and the –nobest flag:

[ec2-user@ip-192-169-2-20 ~]$ sudo dnf list docker-ce --showduplicates | sort -r
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
Last metadata expiration check: 0:40:11 ago on Tue 15 Jun 2021 03:49:08 PM UTC.
docker-ce.x86_64                3:20.10.7-3.el8                 docker-ce-stable
docker-ce.x86_64                3:20.10.6-3.el8                 docker-ce-stable
docker-ce.x86_64                3:20.10.5-3.el8                 docker-ce-stable
docker-ce.x86_64                3:20.10.4-3.el8                 docker-ce-stable
docker-ce.x86_64                3:20.10.3-3.el8                 docker-ce-stable
docker-ce.x86_64                3:20.10.2-3.el8                 docker-ce-stable
docker-ce.x86_64                3:20.10.1-3.el8                 docker-ce-stable
docker-ce.x86_64                3:20.10.0-3.el8                 docker-ce-stable
docker-ce.x86_64                3:19.03.15-3.el8                docker-ce-stable
docker-ce.x86_64                3:19.03.14-3.el8                docker-ce-stable
docker-ce.x86_64                3:19.03.13-3.el8                docker-ce-stable
Available Packages

[ec2-user@ip-192-169-2-20 ~]$ sudo dnf install --nobest docker-ce
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
Last metadata expiration check: 0:40:17 ago on Tue 15 Jun 2021 03:49:08 PM UTC.
Dependencies resolved.
===============================================================================================================================================
 Package                            Architecture Version                                           Repository                             Size
===============================================================================================================================================
Installing:
 docker-ce                          x86_64       3:20.10.7-3.el8                                   docker-ce-stable                       27 M
Installing dependencies:
 container-selinux                  noarch       2:2.162.0-1.module+el8.4.0+11311+9da8acfb         rhui-rhel-8-appstream-rhui-rpms        52 k
 containerd.io                      x86_64       1.4.6-3.1.el8                                     docker-ce-stable                       34 M
 docker-ce-cli                      x86_64       1:20.10.7-3.el8                                   docker-ce-stable                       33 M
 docker-ce-rootless-extras          x86_64       20.10.7-3.el8                                     docker-ce-stable                      9.2 M
 docker-scan-plugin                 x86_64       0.8.0-3.el8                                       docker-ce-stable                      4.2 M
 fuse-common                        x86_64       3.2.1-12.el8                                      rhui-rhel-8-baseos-rhui-rpms           21 k
 fuse-overlayfs                     x86_64       1.4.0-3.module+el8.4.0+11311+9da8acfb             rhui-rhel-8-appstream-rhui-rpms        72 k
 fuse3                              x86_64       3.2.1-12.el8                                      rhui-rhel-8-baseos-rhui-rpms           50 k
 fuse3-libs                         x86_64       3.2.1-12.el8                                      rhui-rhel-8-baseos-rhui-rpms           94 k
 iptables                           x86_64       1.8.4-10.el8                                      rhui-rhel-8-baseos-rhui-rpms          581 k
 libcgroup                          x86_64       0.41-19.el8                                       rhui-rhel-8-baseos-rhui-rpms           70 k
 libnetfilter_conntrack             x86_64       1.0.6-5.el8                                       rhui-rhel-8-baseos-rhui-rpms           65 k
 libnfnetlink                       x86_64       1.0.1-13.el8                                      rhui-rhel-8-baseos-rhui-rpms           33 k
 libnftnl                           x86_64       1.1.5-4.el8                                       rhui-rhel-8-baseos-rhui-rpms           83 k
 libslirp                           x86_64       4.3.1-1.module+el8.4.0+11311+9da8acfb             rhui-rhel-8-appstream-rhui-rpms        69 k
 policycoreutils-python-utils       noarch       2.9-9.el8                                         rhui-rhel-8-baseos-rhui-rpms          251 k
 slirp4netns                        x86_64       1.1.8-1.module+el8.4.0+11311+9da8acfb             rhui-rhel-8-appstream-rhui-rpms        51 k
Enabling module streams:
 container-tools                                 rhel8

Transaction Summary
===============================================================================================================================================
Install  18 Packages

Total download size: 108 M
Installed size: 441 M
Is this ok [y/N]: y
Downloading Packages:

firewalld is already disabled so we don’t need to disable it to address concerns about DNS resolution working inside Docker containers.

Add my user to the docker group and start/enable the docker daemon.

$ sudo usermod -aG docker ec2-user
$ sudo systemctl enable --now docker
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /usr/lib/systemd/system/docker.service.

$ systemctl is-active docker
active
$ systemctl is-enabled docker
enabled
[ec2-user@ip-192-169-2-20 ~]$ cat /etc/redhat-release && docker --version
Red Hat Enterprise Linux release 8.4 (Ootpa)
Docker version 20.10.7, build f0df350

Test docker with hello-world.

Automated Install with Ansible

As I have a number of servers to repeat the installation on, I’ll use an ansible playbook.

[ec2-user@ip-192-169-2-108 ansible-rhel8]$ ansible --version
ansible 2.9.10
  config file = None
  configured module search path = ['/home/ec2-user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.6/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.6.8 (default, Mar 18 2021, 08:58:41) [GCC 8.4.1 20200928 (Red Hat 8.4.1-1)]

[ec2-user@ip-192-169-2-108 ansible-rhel8]$ cat /etc/hosts | grep Rancher  | awk '{print $2}' > inv
[ec2-user@ip-192-169-2-108 ansible-rhel8]$ vi inv
[ec2-user@ip-192-169-2-108 ansible-rhel8]$ cat inv
[rancher]
DevRHEL8-Rancher-01
DevRHEL8-Rancher-02
DevRHEL8-Rancher-03

In the playbook I’m also taking care of some Rancher prerequisites and other tasks

[ec2-user@ip-192-169-2-108 ansible-rhel8]$ cat docker-rancher/tasks/main.yaml
---

- name: Upgrade all packages
  dnf:
    name: "*"
    state: latest
  tags: [update_packages]

- name: Install packages
  dnf:
    name:
      - psacct
      - git
      - yum-utils
      - device-mapper-persistent-data
      - lvm2
      - vim
    state: present
  tags: [dnf_installs]

- name: Enable docker-ce repo
  shell: dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
  tags: [docker_repo]

- name: Install docker
  dnf:
    name: docker-ce
    state: present
  tags: [docker_install]

- name: enable docker service
  systemd:
    name: docker
    state: restarted
    enabled: yes
    daemon_reload: yes
  tags: [docker_restart]

- name: Update sshd_config AllowAgentForwarding
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^#AllowAgentForwarding yes'
    line: 'AllowAgentForwarding yes'
  tags: [rancher-prereq]

- name: Update sshd_config AllowTcpForwarding
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^#AllowTcpForwarding yes'
    line: 'AllowTcpForwarding yes'
  tags: [rancher-prereq]

- name: Update sshd_config GatewayPorts
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^#GatewayPorts no'
    line: 'GatewayPorts yes'
  tags: [rancher-prereq]

- name: check bridge networking is allowed
  shell: modprobe br_netfilter
  tags: [bridge]

- name: check bridge networking is allowed bridge-nf-call-iptables
  shell: echo "1" > /proc/sys/net/bridge/bridge-nf-call-iptables
  tags: [bridge]

- name: Add Kubernetes repo
  yum_repository:
    name: kubernetes
    description: Kubernetes repo
    file: kubernetes
    baseurl: https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
    enabled: yes
    gpgcheck: 1
    repo_gpgcheck: 1
    gpgkey: https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
  tags: [k8srepo]

- name: Install packages
  dnf:
    name:
      - kubectl
    state: present
  tags: [kubectl_install]

- name: Update /etc/hosts
  copy:
    src: /etc/hosts
    dest: /etc/hosts
    mode: '0644'
  tags: [hosts_file]

Running the playbook:

[ec2-user@ip-192-169-2-108 ansible-rhel8]$ ansible-playbook -i ./inv docker-rancher.yaml -b

PLAY [rancher] ********************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************
ok: [DevRHEL8-Rancher-03]
ok: [DevRHEL8-Rancher-02]
ok: [DevRHEL8-Rancher-01]

TASK [docker-rancher : Upgrade all packages] **************************************************************************************************
changed: [DevRHEL8-Rancher-02]
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-01]

TASK [docker-rancher : Install packages] ******************************************************************************************************
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-02]
changed: [DevRHEL8-Rancher-01]

TASK [docker-rancher : Enable docker-ce repo] *************************************************************************************************
[WARNING]: Consider using the dnf module rather than running 'dnf'.  If you need to use command because dnf is insufficient you can add 'warn:
false' to this command task or set 'command_warnings=False' in ansible.cfg to get rid of this message.
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-02]
changed: [DevRHEL8-Rancher-01]

TASK [docker-rancher : Install docker] ********************************************************************************************************
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-01]
changed: [DevRHEL8-Rancher-02]

TASK [docker-rancher : enable docker service] *************************************************************************************************
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-02]
changed: [DevRHEL8-Rancher-01]

TASK [docker-rancher : Update sshd_config AllowAgentForwarding] *******************************************************************************
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-01]
changed: [DevRHEL8-Rancher-02]

TASK [docker-rancher : Update sshd_config AllowTcpForwarding] *********************************************************************************
changed: [DevRHEL8-Rancher-01]
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-02]

TASK [docker-rancher : Update sshd_config GatewayPorts] ***************************************************************************************
changed: [DevRHEL8-Rancher-02]
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-01]

TASK [docker-rancher : check bridge networking is allowed] ************************************************************************************
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-02]
changed: [DevRHEL8-Rancher-01]

TASK [docker-rancher : check bridge networking is allowed bridge-nf-call-iptables] ************************************************************
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-02]
changed: [DevRHEL8-Rancher-01]

TASK [docker-rancher : Add Kubernetes repo] ***************************************************************************************************
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-02]
changed: [DevRHEL8-Rancher-01]

TASK [docker-rancher : Install packages] ******************************************************************************************************
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-02]
changed: [DevRHEL8-Rancher-01]

TASK [docker-rancher : Update /etc/hosts] *****************************************************************************************************
changed: [DevRHEL8-Rancher-03]
changed: [DevRHEL8-Rancher-01]
changed: [DevRHEL8-Rancher-02]

PLAY RECAP ************************************************************************************************************************************
DevRHEL8-Rancher-01        : ok=14   changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
DevRHEL8-Rancher-02        : ok=14   changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
DevRHEL8-Rancher-03        : ok=14   changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Test as before with ‘docker run hello-world’ and verify docker version:

[ec2-user@ip-192-169-2-7 ~]$ cat /etc/redhat-release && docker --version
Red Hat Enterprise Linux release 8.4 (Ootpa)
Docker version 20.10.7, build f0df350

Scripted Install

Rancher provide a handy install script available at https://releases.rancher.com/install-docker/20.10.sh

[ec2-user@ip-192-169-2-250 ~]$ curl  https://releases.rancher.com/install-docker/20.10.sh | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 17683  100 17683    0     0  46904      0 --:--:-- --:--:-- --:--:-- 46904
# Executing docker install script, commit: 7cae5f8b0decc17d6571f9f52eb840fbc13b2737
+ sudo -E sh -c 'yum install -y -q yum-utils'
+ sudo -E sh -c 'yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
Adding repo from: https://download.docker.com/linux/centos/docker-ce.repo
+ '[' stable '!=' stable ']'
+ '[' rhel = rhel ']'
+ adjust_repo_releasever 8.2
+ DOWNLOAD_URL=https://download.docker.com
+ case $1 in
+ releasever=8
+ for channel in "stable" "test" "nightly"
+ sudo -E sh -c 'yum-config-manager --setopt=docker-ce-stable.baseurl=https://download.docker.com/linux/centos/8/\$basearch/stable --save'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
+ sudo -E sh -c 'yum-config-manager --setopt=docker-ce-stable-debuginfo.baseurl=https://download.docker.com/linux/centos/8/debug-\$basearch/stable --save'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
+ sudo -E sh -c 'yum-config-manager --setopt=docker-ce-stable-source.baseurl=https://download.docker.com/linux/centos/8/source/stable --save'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
+ for channel in "stable" "test" "nightly"
+ sudo -E sh -c 'yum-config-manager --setopt=docker-ce-test.baseurl=https://download.docker.com/linux/centos/8/\$basearch/test --save'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
+ sudo -E sh -c 'yum-config-manager --setopt=docker-ce-test-debuginfo.baseurl=https://download.docker.com/linux/centos/8/debug-\$basearch/test --save'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
+ sudo -E sh -c 'yum-config-manager --setopt=docker-ce-test-source.baseurl=https://download.docker.com/linux/centos/8/source/test --save'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
+ for channel in "stable" "test" "nightly"
+ sudo -E sh -c 'yum-config-manager --setopt=docker-ce-nightly.baseurl=https://download.docker.com/linux/centos/8/\$basearch/nightly --save'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
+ sudo -E sh -c 'yum-config-manager --setopt=docker-ce-nightly-debuginfo.baseurl=https://download.docker.com/linux/centos/8/debug-\$basearch/nightly --save'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
+ sudo -E sh -c 'yum-config-manager --setopt=docker-ce-nightly-source.baseurl=https://download.docker.com/linux/centos/8/source/nightly --save'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
+ [[ 8.2 =~ 7\. ]]
+ '[' 8.2 == 7 ']'
+ sudo -E sh -c 'yum makecache'
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
Docker CE Stable - x86_64                                                                                      137 kB/s |  14 kB     00:00
Red Hat Update Infrastructure 3 Client Configuration Server 8                                                   35 kB/s | 2.1 kB     00:00
Red Hat Enterprise Linux 8 for x86_64 - AppStream from RHUI (RPMs)                                              22 kB/s | 2.8 kB     00:00
Red Hat Enterprise Linux 8 for x86_64 - BaseOS from RHUI (RPMs)                                                 24 kB/s | 2.4 kB     00:00
Metadata cache created.
INFO: Searching repository for VERSION '20.10.7'
INFO: yum list --showduplicates 'docker-ce' | grep '20.10.7.*el' | tail -1 | awk '{print $2}'
+ '[' -n 20.10.7-3.el8 ']'
+ sudo -E sh -c 'yum install -y -q docker-ce-cli-20.10.7-3.el8'
warning: /var/cache/dnf/docker-ce-stable-fa9dc42ab4cec2f4/packages/docker-ce-cli-20.10.7-3.el8.x86_64.rpm: Header V4 RSA/SHA512 Signature, key ID 621e9f35: NOKEY
Importing GPG key 0x621E9F35:
 Userid     : "Docker Release (CE rpm) <docker@docker.com>"
 Fingerprint: 060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35
 From       : https://download.docker.com/linux/centos/gpg

Installed:
  docker-ce-cli-1:20.10.7-3.el8.x86_64                                  docker-scan-plugin-0.8.0-3.el8.x86_64

+ sudo -E sh -c 'yum install -y -q docker-ce-20.10.7-3.el8'


Installed:
  container-selinux-2:2.162.0-1.module+el8.4.0+11311+9da8acfb.noarch        containerd.io-1.4.6-3.1.el8.x86_64
  docker-ce-3:20.10.7-3.el8.x86_64                                          docker-ce-rootless-extras-20.10.7-3.el8.x86_64
  fuse-common-3.2.1-12.el8.x86_64                                           fuse-overlayfs-1.4.0-3.module+el8.4.0+11311+9da8acfb.x86_64
  fuse3-3.2.1-12.el8.x86_64                                                 fuse3-libs-3.2.1-12.el8.x86_64
  iptables-1.8.4-10.el8.x86_64                                              libcgroup-0.41-19.el8.x86_64
  libnetfilter_conntrack-1.0.6-5.el8.x86_64                                 libnfnetlink-1.0.1-13.el8.x86_64
  libnftnl-1.1.5-4.el8.x86_64                                               libslirp-4.3.1-1.module+el8.4.0+11311+9da8acfb.x86_64
  policycoreutils-python-utils-2.9-9.el8.noarch                             slirp4netns-1.1.8-1.module+el8.4.0+11311+9da8acfb.x86_64

+ '[' -n 1 ']'
+ sudo -E sh -c 'yum install -y -q docker-ce-rootless-extras-20.10.7-3.el8'
+ command_exists iptables
+ command -v iptables
+ start_docker
+ '[' '!' -z ']'
+ '[' -d /run/systemd/system ']'
+ sudo -E sh -c 'systemctl start docker'
+ sudo -E sh -c 'docker version'
Client: Docker Engine - Community
 Version:           20.10.7
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        f0df350
 Built:             Wed Jun  2 11:56:24 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.7
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       b0f5bc3
  Built:            Wed Jun  2 11:54:48 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.6
  GitCommit:        d71fcd7d8303cbf684402823e425e9dd2e99285d
 runc:
  Version:          1.0.0-rc95
  GitCommit:        b9ee9c6314599f1b4a7f497e1f1f856fe433d3b7
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

================================================================================

To run Docker as a non-privileged user, consider setting up the
Docker daemon in rootless mode for your user:

    dockerd-rootless-setuptool.sh install

Visit https://docs.docker.com/go/rootless/ to learn about rootless mode.


To run the Docker daemon as a fully privileged service, but granting non-root
users access, refer to https://docs.docker.com/go/daemon-access/

WARNING: Access to the remote API on a privileged Docker daemon is equivalent
         to root access on the host. Refer to the 'Docker daemon attack surface'
         documentation for details: https://docs.docker.com/go/attack-surface/

================================================================================

[ec2-user@ip-192-169-2-250 ~]$



https://releases.rancher.com/install-docker/20.10.sh

Verify as before:

[ec2-user@ip-192-169-2-250 ~]$ cat /etc/redhat-release && docker --version
Red Hat Enterprise Linux release 8.4 (Ootpa)
Docker version 20.10.7, build f0df350

I’ll use this script in later versions of the ansible playbook.

Wrapping Up

I didn’t expect the process of installing docker on RHEL8 to be so easy, I expected to hit dependency issues, but it seems with the later versions of Docker 20.10 many of the install issues are fixed https://medium.com/nttlabs/docker-20-10-59cc4bd59d37 .

It is still not an ideal situation, Red Hat are unlikely to help with any container related issues on opening a support case where we are running docker and not podman on RHEL8, but docker appears to be stable.

Categories
Cloud

Upgrade PHP from 7.2 to 7.4 on Amazon Linux 2

In response to WordPress warning about the version of PHP, I decided I should upgrade:

My WordPress operates on an EC2 instance as described in a previous blog post and steps to update are described below:

STEP 1: ssh to the EC2 instance with PuTTY or similar (I’m using WSL – Windows Subsystem for Linux to connect from a Ubuntu shell).

STEP 2: Update the system, note this does not update php:

$ sudo yum update

STEP 3: Check the version of PHP and make sure the amazon-linux-extras package is installed:

$ php -v
$ which amazon-linux-extras

STEP 4: Verify the PHP7.x topic is available:

$ sudo amazon-linux-extras | grep php

STEP 5: Disable both the php7.2 and lamp-mariadb10.2-php7.2 topics

$ sudo amazon-linux-extras disable php7.2
$ sudo amazon-linux-extras disable lamp-mariadb10.2-php7.2

If you see the warning “Beware that disabling topics is not supported after they are installed.” it can be safely ignored.

STEP 6: Check the status of available topics

$ sudo amazon-linux-extras | grep php

STEP 7: Enable the php7.4 topic.

$ sudo amazon-linux-extras enable php7.4

STEP 8: Check with

$ sudo amazon-linux-extras | grep php

STEP 9: Update to PHP 7.4. First clean up the metadata and then install php along with any dependencies:

$ sudo yum clean metadata
$ sudo yum install php 

STEP 10: Check the version of PHP

$ php -v

STEP 11: Reboot the instance or just restart apache with:

$ sudo systemctl restart httpd

STEP 12: Test the WordPress site and verify

The steps above were sufficient for my setup but may be different in other environments.

Categories
Cloud

WordPress on Amazon EC2

Hosting this simple WordPress site on AWS:

The Aim:

Deploy a personal website and blog in AWS using a previously registered domain name: ianstacey.net using the free tier, setting up a WordPress site on a single EC2 instance with Amazon RDS for MySQL to run the MySQL database.

For the exercise I mostly followed https://aws.amazon.com/getting-started/hands-on/deploy-wordpress-with-amazon-rds/ with additional tasks, including creating a new VPC, deploying a Load Balancer and updating Route 53.

I also used https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/SSL-on-amazon-linux-2.html , https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/hosting-wordpress.html as reference and reviewed Lab exercises performed as part of Adrian Cantrill’s https://learn.cantrill.io/p/aws-certified-solutions-architect-associate-saa-c02 course.

The Architecture
The Architecture

1. VPC:

My first step was to create a new VPC in eu-central-1 Frankfurt with a 10.84.0.0/16 CIDR and four subnets, 2 public and 2 private across 2 Availability Zones.

2. Creating a MySQL Database with RDS:

After deleting my first DB as it was associated with all four of my subnets, I created a Subnet Group containing my two private subnets before creating my new DB instance.

Next I created the RDS database, with MySQL as the engine, choosing the latest available version of MySQL and selecting the free tier.

Stepping through the options I set:

  • My DB instance identifier.
  • The master username and password for my database.
  • Instance class and storage details, as I’m using the free tier options are limited.
  • The configure connectivity and network configuration, choosing my new VPC and Subnet Group
  • Created a new Security Group, for now without any Inbound rules set, this will come later
  • Set the initial database name

3. Creating an EC2 Instance:

Next to create an Amazon EC2 instance to run the WordPress site, selecting Launch instance to open the instance creation wizard and selecting

  • The AMI of Amazon Linux 2
  • An instance type of t2.micro, free tier-eligible
  • Selecting my VPC and a public subnet
  • Minimal storage
  • Added a Name Tag
  • Configured a new Security, limiting ssh access from only my home and office IP and allowing HTTP from outside for initial testing
  • Created and downloaded a new key pair

4. Configuring Your RDS Database

At this point I’ve created my RDS database and my EC2 instance, the next action is to configure the RDS database.

  • ssh to the EC2 instance with the .pem file downloaded earlier and install a MySQL client to interact with the database
sudo yum install -y mysql
  • Find the hostname for the RDS database in the AWS console. In the details of the RDS database, the hostname will be shown as the Endpoint in the Connectivity & security section.
  • Use the dig command from the EC2 instance to check DNS
  • In the terminal, enter the following command to set an environment variable for the MySQL host. Be sure to replace “<your-endpoint>” with the hostname of the RDS instance.
export MYSQL_HOST=<your-endpoint>
  • Next, run the following command in the terminal to connect to the MySQL database. Replace “<user>” and “<password>” with the master username and password configured when creating the RDS database.
mysql --user=<user> --password=<password> wordpress
  • If connected successfully, the terminal should indicate connection to the MySQL database as shown in the following image.
  • Finally, create a database user for the WordPress application and give it permission to access the “wordpress” database. Run the following commands, substituting “wordpress-pass” for a strong password :
CREATE USER 'wordpress' IDENTIFIED BY 'wordpress-pass';
GRANT ALL PRIVILEGES ON wordpress.* TO wordpress;
FLUSH PRIVILEGES;
Exit
An alternative method to the above is to Configure Authentication Variables. 

DBName='wordpress'
DBUser='wordpress'
DBPassword='REPLACEME'
DBRootPassword='REPLACEME'

STEP: Create WordPress DB

echo "CREATE DATABASE $DBName;" >> /tmp/db.setup
echo "CREATE USER '$DBUser'@'localhost' IDENTIFIED BY '$DBPassword';" >> /tmp/db.setup
echo "GRANT ALL ON $DBName.* TO '$DBUser'@'localhost';" >> /tmp/db.setup
echo "FLUSH PRIVILEGES;" >> /tmp/db.setup
mysql -u root --password=$DBRootPassword < /tmp/db.setup
sudo rm /tmp/db.setup

5. Install Apache on EC2

To run WordPress, you need to run a web server on your EC2 instance. Apache and Nginx are popular web servers used with WordPress, choosing the best one is a matter of some debate.

  • I’ve installed Apache on the EC2 instance with the following command:
sudo yum install -y httpd
  • To start the Apache web server, run the following commands in the terminal:
sudo service httpd enable
sudo service httpd start
  • Confirm Apache is working by browsing to the Public IP of the EC2 instance

6. Download and Configure WordPress on EC2

In this step, we will download the WordPress software and set up the configuration.

First, download and uncompress the software by running the following commands in the terminal:

wget https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz

If you run “ls” to view the contents of your directory, you will see a tar file and a directory called wordpress with the uncompressed contents.

$ ls
latest.tar.gz  wordpress

Change into the wordpress directory and create a copy of the default config file using the following commands:

cd wordpress
cp wp-config-sample.php wp-config.php

Then, open the wp-config.php file using vi:

vi wp-config.php

You need to edit two areas of configuration.

First, edit the database configuration by changing the following lines:

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'database_name_here' );

/** MySQL database username */
define( 'DB_USER', 'username_here' );

/** MySQL database password */
define( 'DB_PASSWORD', 'password_here' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

The values should be:

  • DB_NAME: “wordpress”
  • DB_USER: The name of the user you created in the database in the previous module
  • DB_PASSWORD: The password for the user you created in the previous module.
  • DB_HOST: The hostname of the database that you found in the previous module.

The second configuration section you need to configure is the Authentication Unique Keys and Salts. It looks as follows in the configuration file:

/**#@+
 * Authentication Unique Keys and Salts.
 *
 * Change these to different unique phrases!
 * You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
 * You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
 *
 * @since 2.6.0
 */
define( 'AUTH_KEY',         'put your unique phrase here' );
define( 'SECURE_AUTH_KEY',  'put your unique phrase here' );
define( 'LOGGED_IN_KEY',    'put your unique phrase here' );
define( 'NONCE_KEY',        'put your unique phrase here' );
define( 'AUTH_SALT',        'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT',   'put your unique phrase here' );
define( 'NONCE_SALT',       'put your unique phrase here' );

Go to this link to generate values for this configuration section. Replace the entire content in that section with the content from the link. Save and exit from vi.

With the configuration updated, we are almost ready to deploy the WordPress site. In the next step, we will make your WordPress site live.

7. Deploying WordPress

In this step, we will download the WordPress software and set up the configuration, making the Apache web server handle requests for WordPress.

First, install the application dependencies we need for WordPress. In the terminal, run the following command.

sudo amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2

Second, change to the proper directory by running the following command:

cd /home/ec2-user

Then, copy your WordPress application files into the /var/www/html directory used by Apache.

sudo cp -r wordpress/* /var/www/html/

Finally, restart the Apache web server to pick up the changes.

sudo service httpd restart

You should see the WordPress welcome page and the five-minute installation process.

8. Enable HTTPS / Load balancers and Update Route 53

Next I wanted to switch to HTTPS to access the site through https://www.ianstacey.net

  • Created a new Elastic Load Balancer with HTTP and HTTPS support
  • Configured my Security Settings, requesting a new certificate from AWS Certificate Manager (ACM), using DNS validation.
  • Configure the Security Group
  • Configured the Routing
  • Created my Target Groups for the ELB, registered the target EC2 instance and configured the Health Checks
  • Updated Route 53 to point www.ianstacey.net and blog.ianstacey.net to the new ELB.
ACM Cert Request

Issues:

After configuring the above I hit a couple of issues. Firstly the website was only displaying simple text. Once I fixed that I found links were trying to leave ianstacey.net and follow the Public IP of the instance.

To help isolate and resolve the second issue, I created a very simple static website with a couple of links:

[ec2-user@ip-10-84-1-125 html-static]$ cat index.html
 <!DOCTYPE html>
<html>
<title>HTML Tutorial is</title>
<body>

<h1>This is a heading yes</h1>
<p>This is a paragraph oh boy.</p>

 <a href="https://www.w3schools.com/">Visit W3Schools.com!</a>
 <br>
 <a href=link1.html>Visit Link1</a>
 <br>
 <a href=link2.html>Visit Link2</a>

</body>
</html>
[ec2-user@ip-10-84-1-125 html-static]$ cat link1.html
 <!DOCTYPE html>
<html>
<title>HTML Tutorial LINK1 </title>
<body>

<h1>This is a heading LINK1 </h1>
<p>This is a paragraph LINK1</p>

</body>
</html>
[ec2-user@ip-10-84-1-125 html-static]$ cat link2.html
 <!DOCTYPE html>
<html>
<title>HTML Tutorial LINK2 </title>
<body>

<h1>This is a heading LINK2 </h1>
<p>This is a paragraph LINK2</p>

</body>
</html>
[ec2-user@ip-10-84-1-125 html-static]$

Then resolved the WordPress settings, switching my apache root back to WordPress.

9. Next Steps

  • Explore WordPress and its available themes and populate this site.
  • Secure the site, I have the WP Cerber plugin enabled to help against brute-force attacks on the wp-admin page, limiting login attempts to 5 before blocking an IP address. Attempts to hide the wp-admin with https://wordpress.org/plugins/wps-hide-login/ did not work for me, so I plan to revisit this.
  • Deploy a second EC2 instance in a different AZ and update my ELB Target Groups.
  • Tear everything down and redeploy with CloudFormation.
  • Monitor costs with Cost Explorer to ensure I keep within the free tier with Billing Alerts configured.
Limiting login attempts.
Blocking an IP.

July 2021 Update: