Tutorial container wordpress com docker compose e suporte SSL
Objetivo
Este tutorial tem o objetivo ensinar um passo a passo de como instalar o wordpress como um container utilizando docker, com suporte a um nome de domínio e acesso por http e https. Por padrão o container do wordpress não vem com suporte SSL. Este tutorial aborda a possibilidade de instalar vários containers do wordpress para diversos sites e domínios distintos.
Requisitos
- Software docker instalado e configurado no sistema operacional.
- Docker compose instalado e configurado.
- Conexão com internet para download da imagem do wordpress, mariadb (mysql), phpmyadmin, certificado SSL e proxy reverso (apache ou nginx).
- Configuração DNS em seu domínio, direcionando para seu servidor.
- Caso utilize um domínio local, editar o arquivo /etc/hosts em seu computador e adicioná-lo apropriadamente.
- Se utilizar um servidor caseiro, garantir que seu roteador está redirecionando as portas 80 e 443 para seu servidor.
- Garantir que seu firewall no servidor não está bloqueando as portas 80 e 443.
Observação
Como temos desejo de configurar um domínio em nossa instalação do wordpress, é comum utilizarmos um proxy reverso, para gerenciar uma solicitação pela porta 80 ou 443 e encaminhar para a porta correspondente que estará mapeada no container. Vamos abordar o arquivo de configuração de proxy reverso tanto no apache (apache2) quanto no nginx (Nginx Proxy Manager).
Como instalar imagem do wordpress com docker compose tendo suporte a certificado SSL e nome de domínio
Passo 1: Configuração do Servidor de Páginas para proxy reverso
Precisamos preparar nosso ambiente de hospedagem para gerenciar as solicitações http/https e redirecionar para os containers que queremos criar. Caso já tenha o apache instalado em seu servidor, independente de ser em um container ou não, a etapa principal é configurar corretamente o arquivo de virtualhost do domínio com o proxy reverso. Caso tenha preferência por utilizar o container do nginx proxy manager, as etapas são todas em ambiente web e mais fáceis.
Neste exemplo, vamos supor que queremos instalar o wordpress para o subdomínio dev.viniciuspaes.com. Mas poderia ser para qualquer domínio ou subdomínio que escolher.
Agora vamos definir quais as portas que este container wordpress irá utilizar. Para esse domínio, vamos utilizar a porta 9080 para acesso http, a porta 9081 para acesso com SSL (https) e porta 8082 para acesso a interface do phpmyadmin.
Opção 1 – Configuração caso utilize o servidor apache instalado fora do container
É necessário 2 arquivos de configuração instalados no apache: um para acesso http e outro para acesso https.
Para acesso http, vamos criar o arquivo dev.viniciuspaes.com.http.conf:
sudo nano /etc/apache2/sites-available/dev.viniciuspaes.com.http.conf
Agora cole o conteúdo abaixo dentro do arquivo. Lembre-se de alterar o nome de domínio e a porta que irá utilizar:
<VirtualHost *:80> ServerName dev.viniciuspaes.com ServerAdmin postmaster@viniciuspaes.com ProxyPass / http://127.0.0.1:9080/ ProxyPassReverse / http://127.0.0.1:9080/ ProxyPreserveHost On ErrorLog ${APACHE_LOG_DIR}/dev.viniciuspaes.com.error.log CustomLog ${APACHE_LOG_DIR}/dev.viniciuspaes.com.access.log combined ## Redirecionar tráfego HTTP para HTTPS, habilitar somente após configurar certificado SSL # RewriteEngine on # RewriteCond %{SERVER_NAME} =dev.viniciuspaes.com # RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] </VirtualHost>
No arquivo acima, escolhemos a porta 9080 para receber solicitações http no container que ainda vai ser criado.
Agora é necessário solicitar um certificado SSL do letsencrypt pelo certbot.
Caso seu certbot não esteja em um container basta seguir os passos deste tutorial:
Ao solicitar o certificado as chaves serão salvas no caminho:
- /etc/letsencrypt/live/dev.viniciuspaes.com/fullchain.pem
- /etc/letsencrypt/live/dev.viniciuspaes.com/privkey.pem
Um arquivo de configuração para o apache também será criado com o nome:
- dev.viniciuspaes.com.http-le-ssl.conf
Vamos agora editar este arquivo para adicionar as informações do proxy reverso:
sudo nano /etc/apache2/sites-available/dev.viniciuspaes.com.http-le-ssl.conf
O conteúdo do arquivo deverá ficar como:
<IfModule mod_ssl.c> <VirtualHost *:443> ServerName dev.viniciuspaes.com SSLProxyEngine on <Proxy *> Allow from all </Proxy> ProxyPreserveHost On ProxyRequests Off ProxyPass / https://127.0.0.1:9081/ ProxyPassReverse / https://127.0.0.1:9081/ ErrorLog ${APACHE_LOG_DIR}/dev.viniciuspaes.com.error.log CustomLog ${APACHE_LOG_DIR}/dev.viniciuspaes.com.access.log combined SSLCertificateFile /etc/letsencrypt/live/dev.viniciuspaes.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/dev.viniciuspaes.com/privkey.pem Include /etc/letsencrypt/options-ssl-apache.conf </VirtualHost> </IfModule>
Lembrando que no arquivo acima escolhemos a porta 9081 no container, para receber solicitação https (SSL).
Opção 2 – Configuração caso utilize container com Nginx Proxy Manager
Levando em consideração que temos o nginx proxy manager instalado em um container, os passos para criação do proxy reverso e solicitação do certificado SSL são simples. Um exemplo de docker compose para instalação do Nginx Proxy Manager:
services: app: image: 'jc21/nginx-proxy-manager:latest' container_name: nginx_proxy ports: - '80:80' - '81:81' - '443:443' volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt restart: unless-stopped db: image: mariadb:latest container_name: nginx_proxy_db environment: MYSQL_ROOT_PASSWORD: 'senha_root_do_banco_mudar' MYSQL_DATABASE: 'npm' MYSQL_USER: 'npm' MYSQL_PASSWORD: 'senha_do_banco_mudar' volumes: - ./mysql-data:/var/lib/mysql restart: unless-stopped networks: default: external: true name: rede_proxy_reverso
Faça login na interface do NPM (nginx proxy manager).
Clique no texto:
Proxy Hosts
Agora clique no botão:
Add proxy host
Em domain names vamos adicionar nosso domínio (neste caso um subdomínio):
dev.viniciuspaes.com
Em scheme, vamos optar pelo acesso com SSL, então vamos escolher:
https
Na opção de foward hostname / IP, devemos escolher o IP do servidor onde o container do wordpress será instalado. Atenção, não confundir e escolher o IP interno do container. Como o container será instalado no mesmo servidor onde o NPM está instalado, podemos utilizar o ip de loopback (localhost):
127.0.0.1
Como definimos que o container que será criado irá responder solicitação https na porta 8081, vamos utilizá-la na opção Foward Port:
8081
Neste mesmo modal de opção, temos um pequeno menu com as opçoes:
Details | Custom locations | SSL | Advanced
Vamos selecionar a aba:
SSL
Vá na opção de SSL Certificate (vai estar em None por padrão) e escolha a opção:
Request a new SSL Certificate with Let's Encrypt
Tudo pronto e configurado. Basta clicar no botão:
Save
Passo 2: Criação de arquivos para configurar o container do wordpress
Vamos supor o desejo de manter todos os arquivos do containers organizados em uma pasta chamada docker, que pode estar dentro da nossa pasta pessoal. Vamos criar então uma pasta para nosso container do wordpress:
mkdir ~/docker/wordpress
Agora precisamos criar 5 arquivos:
- Dockerfile – este arquivo vai conter algumas tarefas que precisam ser utilizadas no momento de criação do container
- .env – este arquivo contém a definição de algumas variáveis importantes no processo de setup dos containers: nome de usuário, senha, nome de domínio, etc.
- 000-default.conf – é o arquivo de configuração que o servidor apache que vem dentro do container do wordpress deve utilizar para acesso http
- default-ssl.conf – semelhante ao arquivo 000-default.conf, porém com as configurações de acesso por ssl
- docker-compose.yml – arquivo de configuração dos containers a serem criados
Vamos caminhar até a pasta que criamos para guardar as informações deste container:
cd ~/docker/wordpress
Para criar o arquivo Dockerfile, usamos o comando:
nano Dockerfile
Dentro do arquivo vamos inserir o conteúdo:
#Definir a imagem do container a ser utilizado como base FROM wordpress:latest #Habilitar arquivo php.ini de produção caso existente na imagem RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" #Aumentar o post_max_size de 8MB para 64MB RUN sed -i -e "s/post_max_size \= 8M/post_max_size \= 64M/g" "$PHP_INI_DIR/php.ini" #Aumentar o upload_max_filesize de 2M para 60MB RUN sed -i -e "s/upload_max_filesize \= 2M/upload_max_filesize \= 60M/g" "$PHP_INI_DIR/php.ini" #Aumentar o tempo da sessão de 1440s para 86400s RUN sed -i -e "s/session.gc_maxlifetime \= 1440/session.gc_maxlifetime \= 86400/g" "$PHP_INI_DIR/php.ini" #Modificar o User ID e o Group ID do usuário www-data de dentro do container para seu usuário fora do container RUN usermod -u 1000 www-data && groupmod -g 1000 www-data #Copiar o arquivo defaul-ssl.conf que criamos para dentro do container ADD default-ssl.conf /etc/apache2/sites-available/default-ssl.conf Copiar o arquivo 000-default.conf que criamos, para dentro do container ADD 000-default.conf /etc/apache2/sites-available/000-default.conf #Habiliar o virtualhost definido em default-ssl.conf RUN a2ensite default-ssl.conf #Habilitar o virtualhost definido em 000-default.conf RUN a2ensite 000-default.conf #Caso sua imagem não tenha mysqli instalado (php:8.3-apache por exemplo), descomente o comando abaixo #RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli # Habilitar módulos rewrite, header, expires e ssl RUN a2enmod rewrite && a2enmod headers && a2enmod expires && a2enmod ssl #Adicionar o Servername do seu nome de domínio no arquivo apache2.conf RUN echo "ServerName dev.viniciuspaes.com" >> /etc/apache2/apache2.conf
Vamos criar o segundo arquivo da lista:
nano .env
Vamos colar o conteúdo abaixo dentro do arquivo:
MYSQL_USER=nome_usuario_banco MYSQL_PASSWORD=senha_do_banco_mudar MYSQL_DB=nome_banco_dados DOMAIN=dev.viniciuspaes.com WP_PREFIX=wp_ USER=1000 GROUP=1000
Para o terceiro arquivo necessário, vamos criá-lo com o comando:
nano 000-default.conf
Seu conteúdo deve ser equivalente ao código abaixo:
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Para o quarto arquivo necessário, vamos criar o arquivo de configuração para acesso https:
nano default-ssl.conf
Se conteúdo deve conter:
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;"
Header append X-FRAME-OPTIONS "SAMEORIGIN"
Header set X-Content-Type-Options nosniff
Header set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "no-referrer"
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLEngine on
#SSLCertificateFile /etc/apache2/ssl-certs/cert-filename.cert
#SSLCertificateKeyFile /etc/apache2/ssl-certs/cert-filenamekey.key
SSLCertificateFile /etc/apache2/ssl-certs/server.crt
SSLCertificateKeyFile /etc/apache2/ssl-certs/server.key
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory /usr/lib/cgi-bin>
SSLOptions +StdEnvVars
</Directory>
BrowserMatch "MSIE [2-6]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
</VirtualHost>
</IfModule>
Por fim, vamos criar o arquivo docker-compose.yml:
nano docker-compose.yml
Cole o código abaixo dentro do arquivo:
services: db: image: mariadb:latest container_name: ${DOMAIN}_db command: '--default-authentication-plugin=mysql_native_password' volumes: - ./db_data:/var/lib/mysql restart: always env_file: .env environment: - MYSQL_USER=${MYSQL_USER} - MYSQL_DATABASE=${MYSQL_DB} - MYSQL_PASSWORD=${MYSQL_PASSWORD} - MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD} wordpress: container_name: ${DOMAIN}_web env_file: - .env depends_on: - db build: . volumes: - ./public_html:/var/www/html/ - ./logs:/var/log/apache2 - ./000-default.conf:/etc/apache2/sites-available/000-default.conf - ./default-ssl.conf:/etc/apache2/sites-available/default-ssl.conf - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /etc/letsencrypt/live/${DOMAIN}/fullchain.pem:/etc/apache2/ssl-certs/server.crt - /etc/letsencrypt/live/${DOMAIN}/privkey.pem:/etc/apache2/ssl-certs/server.key ports: - "9080:80" - "9081:443" restart: always environment: - WORDPRESS_DB_HOST=db:3306 #nome do container:porta que proverá recursos de banco de dados, neste caso o container chama db e a porta padrão é a 3306 - WORDPRESS_DB_USER=${MYSQL_USER} - WORDPRESS_DB_PASSWORD=${MYSQL_PASSWORD} - WORDPRESS_DB_NAME=${MYSQL_DB} - WORDPRESS_TABLE_PREFIX=${WP_PREFIX} extra_hosts: - "${DOMAIN}:127.0.1.1" #user: ${USER}:${GROUP} #linha comentada, mas caso necessário configura UID e GID de permissão dos arquivos de acordo com definido no .env phpmyadmin: container_name: ${DOMAIN}-phpmyadmin image: linuxserver/phpmyadmin env_file: .env environment: PMA_HOST: db #nome do container que proverá recursos de banco de dados, neste caso o container chama db PMA_PORT: 3306 MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD} ports: - "9082:80"
Passo 3: Confirmar caminho de certificados SSL gerados no Passo 1
Neste passo, é importante verificar qual a estratégia que utilizou para gerar o certificado SSL para seu domínio.
Se possui o certbot instalado no seu servidor, os certificados vão estar na pasta:
/etc/letsencrypt/live/nome_domínio/
Se utilizou o Nginx Proxy Manager, seu certificado está na pasta que definiu no container, neste exemplo, seria algo como:
~/docker/nginx-proxy/letsencrypt/live/nome_domínio/
Caso tenha utilizado um container somente para o certbot, este também costuma utilizar a pasta:
/etc/letsencrypt/live/nome_domínio/
Então é interessante verificar qual opção utilizou para gerar os certificados. Esta etapa é importante para atualizar o arquivo docker-compose.yml do passo anterior:
volumes: - ./public_html:/var/www/html/ - /etc/letsencrypt/live/${DOMAIN}/fullchain.pem:/etc/apache2/ssl-certs/server.crt - /etc/letsencrypt/live/${DOMAIN}/privkey.pem:/etc/apache2/ssl-certs/server.key
Observação! É possível que seus certificados estejam com o diretório criado com sufixo no nome, do tipo: -0001. Exemplo: /etc/letsencrypt/live/dev.viniciuspaes.com-0001/. Ficar atento a esta possibilidade, para definir o caminho correto para o domínio.
Passo 4: Confirmar criação entradas DNS
Neste exemplo vamos configurar um subdomínio para funcionar em um container com wordpress e com suporte SSL. Desta forma, em nossa registrar, precisamos ter uma entrada que converte um nome de domínio ou subdomínio para o ip do nosso servidor. Após criar a entrada, podemos verificar se a mesma está funcionar com o comando:
dig dev.viniciuspaes.com
Vamos ter como saída algo semelhante a:
; <<>> DiG Ubuntu <<>> dev.viniciuspaes.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30 ;; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; COOKIE: 4f06f2012983012830129120398124d58fd5aac75eded21cf (good) ;; QUESTION SECTION: ;dev.viniciuspaes.com. IN A ;; ANSWER SECTION: dev.viniciuspaes.com. 120 IN A 172.67.148.152
Na saída do comando dig acima, podemos ver que exista uma entrada DNS do tipo A configurada, direcionando o nome do subdomínio para o IP do servidor.
Passo 5: Executar o container
Com os passos anteriores configurados corretamente, podemos iniciar nosso container wordpress com suporte tanto http, quanto SSL. Vamos caminhar novamente para a pasta onde está o arquivo docker compose:
cd ~/docker/wordpress
Vamos executar o container com o comando:
docker compose up -d
Lembrete sobre as portas utilizadas:
- Acesso website http: porta 9080
- Acesso website https: porta 9081
- Acesso interface phpmyadmin: porta 9082
Caso o servidor de proxy reverso esteja configurado no mesmo servidor do container é possível omitir as portas da URL nativamente. Se utilizar o NPM (Nginx Proxy Manager), com uma rede interna compartilhada entre o container wordpress e o NPM, você também pode omitir o endereço IP do servidor e colocar o nome do container diretamente no NPM.
Passo 6: Acessar website wordpress pelo nome de domínio
Com o docker-compose.yml executado, é possível verificar se os containers do wordpress, mariadb (mysql) e phpmyadmin estão funcionando corretamente com o comando:
docker ps
Como saída do comando acima, vamos ter algo semelhante ao texto abaixo:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e2yf533e dev-wordpress "docker-entrypoint.s…" 22 seconds ago Up 20 seconds 9080->80/tcp, 9081->443/tcp, dev.viniciuspaes.com_web 47g5b597 mariadb:latest "docker-entrypoint.s…" 23 seconds ago Up 21 seconds 3306/tcp dev.viniciuspaes.com_db 224907aa phpmyadmin "/init" 23 seconds ago Up 21 seconds 443/tcp, 9082->80/tcp, 9082->80/tcp dev.viniciuspaes.com-phpmyadmin
Se na saída acima, na coluna de STATUS não receber nenhum tipo de erro. Aparentemente os containers estão funcionais.
Abra agora o seu browser de internet e acesse o nome de domínio que atribuiu ao container. No caso desse exemplo, vamos acessar:
https://dev.viniciuspaes.com
Atenção, o container do banco de dados demora alguns segundos a mais para iniciar e ficar funcional, comparado ao container do wordpress. Se acessar rapidamente a url do seu nome de domínio, é comum receber o erro de conexão de banco de dados do wordpress. Só aguardar alguns segundos e recarregar a página.
Conclusão
Existem diversas abordagens para configurar o wodpress em formato de container no docker. O container padrão do wordpress vem com o servidor de páginas apache instalado, porém somente com a porta 80 configurada. Este tutorial abordou uma forma de ativar o virtualhost para ssl no container padrão do wordpress e definiu as configurações necessárias para correto funcionamento.