오라클 클라우드에 Docker 기반 워드프레스 설치 실행 방법

오라클 클라우드 워드프레스 설치 실행 방법 을 자세히 알아보겠습니다.
준비물을 갖추셨다면 실행까지 한시간 정도 예상됩니다.
– 관련글: 수익형 워드프레스 무료 간단 구축 방법
– 이전글: 수익형 워드프레스 구축 오라클 클라우드 가입 방법

brown wooden house and mountain reflecting on lake
Photo by Ricky Esquivel on Pexels.com

 

소개 & 준비물

지난시간엔 워드프레스를 실행하는 개괄적인 내용을 알아보았는데요. 이번 시간엔 오라클 프리티어 클라우드에 워드프레스를 실행하는 상세한 내용을 알아보도록 하겠습니다. 이 가이드는 비전공자분들도 따라하기만 하면 손쉽게 워드프레스를 설치하기 위한 가이드입니다. 그래서 제가 커스텀한 부분에 대해 깊게 설명하기보다는 실용적으로 필요한 부분은 코드로 공유 해드릴게요. 필요한 준비물은 아래와 같습니다. 복잡해보여도 어렵지 않으니 꼼꼼하게 차근차근 따라해보세요!

위 내용은 모두 지난 글에서 다루었는데요.
자세한 링크는 필요한 준비물마다 링크했으니 확인해보고 아래 내용을 따라와주세요!

1단계: 생성된 인스턴스에 접속하기

첫 번째 단계는 생성된 인스턴스에 접속하는 것입니다. SSH 를 사용해 서버에 접근합니다. SSH 를 사용하려면 윈도우의 경우 Putty 를 사용하셔서 IP와 ssh key를 이용하여 접근합니다. 맥의 경우는 Terminal 을 실행하여 아래와 같이 명령어를 입력하면 접속할 수 있습니다.

ssh -i {다운받은 SSH Key 파일경로} ubuntu@{내 서버와 연결된 도메인주소}

## 예) 
ssh -i ~/oraclecloud/ssh-key-2023-06-17.key ubuntu@celebme.net

접근하시면 아래와 같이 우분투 웰컴 메세지를 볼수 있습니다.

2단계: docker 설치

시스템에 접속하였으면 아래 명령어를 한 문단씩 복사&붙여넣고 엔터를 눌러 실행하여 Docker를 설치합니다. 상세한 설치 가이드는 Docker의 원본 설치 가이드를 참고하셔도 됩니다. 중간에 Y(yes) / N (no) 질문을 물어보는 프롬프트가 뜬다면 y와 엔터를 눌러줍니다.

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo apt install docker-compose

sudo groupadd docker

sudo usermod -aG docker $USER

newgrp docker

docker run hello-world

 sudo systemctl enable docker.service
 sudo systemctl enable containerd.service

정상적으로 완료되었다면 아래와 같이 Hello from Docker! 메세지를 확인할 수 있습니다.

Docker 설치가 완료되었습니다.

3단계: http 기반의 wordpress 실행

워드프레스를 보안연결 구성하기 이전에 우선 http로 먼저 실행해보도록 하겠습니다.
관련 폴더를 만들고 설정파일을 작성합니다.

nginx 설정

mkdir wordpress && cd wordpress
mkdir -p php wordpress nginx-conf dbdata certbot-etc

vim nginx-conf/nginx.conf

위 명령어로 폴더를 만들고 nginx.conf 파일을 만들어줍니다.
먼저 아래 내용을 복사합니다.

server {
        listen 80;
        listen [::]:80;


        server_name example.com www.example.com;


        index index.php index.html index.htm;


        root /var/www/html;


        location ~ /.well-known/acme-challenge {
                allow all;
                root /var/www/html;
        }


        location / {
                try_files $uri $uri/ /index.php$is_args$args;
        }


        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass wordpress:9000;
                fastcgi_index index.php;
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $fastcgi_path_info;
        }


        location ~ /\.ht {
                deny all;
        }


        location = /favicon.ico {
                log_not_found off; access_log off;
        }
        location = /robots.txt {
                log_not_found off; access_log off; allow all;
        }
        location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
                expires max;
                log_not_found off;
        }
}

열린 편집기에서 a 를 눌러주고 하단의 — INSERT — 구문을 통해 현재 편집모드인지 확인하고, 화면에 마우스 우클릭하여 Paste(붙여넣기) 를 눌러줍니다.

이제 esc 를 눌러서 편집모드를 빠져나오고, 콜론(:)을 입력합니다.
하단에 — INSERT — 대신 콜론이 표시되면 아래 명령어를 작성하고 엔터를 눌러서
상단의 example.com www.example.com 이 두가지 부분을 자신의 도메인 주소로 자동 변경합니다.

:%s/example.com/자신의도메인주소/g (엔터)

변경되었으면 다시 콜론(:)을 입력하고 x를 누르고 엔터를 눌러줍니다. 파일을 저장하고 빠져나오게 됩니다.

:x

php 설정

php 설정도 위와 비슷하게 설정파일을 작성해줍니다.

vim php/php.ini

아래 구문을 모두 복사하고, 편집기에서 a를 눌러 — INSERT — 를 확인하고 마우스 우클릭하여 붙여넣기 합니다.

short_open_tag = On
memory_limit = 256M
cgi.fix_pathinfo = 0
upload_max_filesize = 100M
post_max_size = 101M
max_execution_time = 360
date.timezone = Asia/Seoul
expose_php = off

max_input_time = 600;
default_socket_timeout = 600;
max_execution_time = 600;

esc를 누르고 콜론+x 후에 엔터를 눌러 저장후 빠져나옵니다.

MySQL 계정설정

MySQL 데이터베이스의 계정설정을 위해 환경변수 파일을 작성합니다.

vim .env

아래 내용을 복사하고, 편집기에서 a를 눌러 — INSERT — 를 확인하고 붙여넣기 합니다.

MYSQL_ROOT_PASSWORD=
MYSQL_USER=
MYSQL_PASSWORD=

이 상태에서 화살표를 눌러 =기호 옆에 자신이 설정할 계정정보를 입력합니다. 띄어쓰기없이 입력합니다.

MYSQL_ROOT_PASSWORD=MYSQL의 root 계정 비밀번호
MYSQL_USER=WordPress에서 사용할 MySQL 계정 ID
MYSQL_PASSWORD=WordPress에서 사용할 MySQL 계정 비밀번호

입력을 마쳤으면 esc를 누르고 콜론+x 후에 엔터를 눌러 저장후 빠져나옵니다.

docker-compose.yml 작성

NginX, PHP, MySQL, WordPress를 아우르는 설정파일을 작성합니다.

vim docker-compose.yml

아래 내용을 복사하고, 편집기에서 a 를 눌러 — INSERT — 를 확인하고 붙여넣기 합니다.

version: '3'


services:


  db:
    image: mariadb:latest
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - MYSQL_DATABASE=wordpress
    volumes:
      - ./dbdata:/var/lib/mysql
    networks:
      - app-network


  wordpress:
    depends_on:
      - db
    image: wordpress:fpm-alpine
    container_name: wordpress
    restart: unless-stopped
    env_file: .env
    environment:
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - ./wordpress:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/php.ini
    networks:
      - app-network


  webserver:
    depends_on:
      - wordpress
    image: nginx:alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./wordpress:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - ./certbot-etc:/etc/letsencrypt
    networks:
      - app-network


  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - ./certbot-etc:/etc/letsencrypt
      - ./wordpress:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email admin@example.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com


volumes:
  certbot-etc:
  wordpress:
  dbdata:
  nginx-conf:


networks:
  app-network:
    driver: bridge

이제 esc 를 눌러서 편집모드를 빠져나오고, 콜론(:)을 입력합니다.
하단에 — INSERT — 대신 콜론이 표시되면 아래 명령어를 작성하고 엔터를 눌러서
상단의 example.com www.example.com 이 두가지 부분을 자신의 도메인 주소로 자동 변경합니다.

:%s/example.com/자신의도메인주소/g (엔터)

변경을 마쳤으면 esc를 누르고 콜론+x 후에 엔터를 눌러 저장후 빠져나옵니다.

실행하기

아래 명령어로 http 기반 워드프레스를 실행합니다.

docker compose up -d

아래와 같이 모두 done이 뜨면 성공입니다.

Creating db ... done
Creating phpmyadmin ... done
Creating wordpress  ... done
Creating webserver  ... done
Creating certbot    ... done

잘 실행되었다면 certbot이 https 연결을 위해 필요한 인증서를 생성 테스트했을 것입니다.
아래 명령어로 확인합니다.

docker compose exec webserver ls -la /etc/letsencrypt/live

위와 같이 도메인주소에 해당하는 인증서가 폴더에 생성된 것을 볼 수 있습니다.

4단계: https 기반의 wordpress 실행

위의 step을 모두 따라오셨다면 http기반 접근이 가능한 상태가 되었을 것입니다. 하지만 http는 보안적으로 취약하고 추후 SEO와 같이 다른 검색엔진에 내 사이트를 연동할 때 불리합니다. 그래서 인증서를 적용하여 보안 연결을 구성해줍니다.

인증서 발급

아래 명령어로 docker-compose.yml 파일을 수정하고 인증서를 발급합니다.

vim docker-compose.yml

콜론(:)을 입력합니다.
하단에 — INSERT — 대신 콜론이 표시되면 아래 명령어를 작성하고 엔터를 눌러서
staging 을 force-renewal 로 변경합니다.

:%s/staging/force-renewal/g (엔터)

변경을 마쳤으면 esc를 누르고 콜론+x 후에 엔터를 눌러 저장후 빠져나옵니다.
아래 명령어로 인증서 발급을 수행합니다.

docker compose up --force-recreate --no-deps certbot

nginx 설정

발급이 정상적으로 완료되면 nginx에 적용하기 위해 ssl 설정을 수정합니다.
아래 구문을 하나씩 수행합니다.

# webserver 중지
docker compose stop webserver

# ssl nginx 설정 다운로드
curl -sSLo nginx-conf/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf

# 기존설정 삭제
rm nginx-conf/nginx.conf

# 신규설정 생성
vim nginx-conf/nginx.conf

신규 생성할 nginx.conf 를 열고 a를 눌러 하단의 — INSERT — 를 확인하고, 아래 내용을 복사하여 붙여넣기 합니다.

server {
        listen 80;
        listen [::]:80;


        server_name example.com www.example.com;


        location ~ /.well-known/acme-challenge {
                allow all;
                root /var/www/html;
        }


        location / {
                rewrite ^ https://$host$request_uri? permanent;
        }
}


server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name example.com www.example.com;


        index index.php index.html index.htm;


        root /var/www/html;


        server_tokens off;
        client_max_body_size 100M;


        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
        include /etc/nginx/conf.d/options-ssl-nginx.conf;


        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header Referrer-Policy "no-referrer-when-downgrade" always;
        add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
        # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
        # enable strict transport security only if you understand the implications


        location / {
                try_files $uri $uri/ /index.php$is_args$args;
        }


        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
		fastcgi_connect_timeout  600; #기본 60초
                fastcgi_send_timeout     600; #기본 60초
                fastcgi_read_timeout     600;

                fastcgi_pass wordpress:9000;
                fastcgi_index index.php;
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $fastcgi_path_info;
        }


        location ~ /\.ht {
                deny all;
        }


        location = /favicon.ico {
                log_not_found off; access_log off;
        }
        location = /robots.txt {
		try_files $uri $uri/ /index.php?$args /?robots=1;
                log_not_found off; access_log off; allow all;
        }
        location = /sitemap.xml {
		try_files $uri $uri/ /index.php?$args /index.php?xml_sitemap=params=;
                log_not_found off; access_log off; allow all;
        }
        location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
                expires max;
                log_not_found off;
        }

	# BEGIN W3TC Browser Cache
        gzip on;

        # compression level (1-9)
        # 6 is a good compromise between CPU usage and file size
        gzip_comp_level 6;

        gzip_buffers 16 8k;

        # minimum file size limit in bytes to avoid negative compression outcomes
        gzip_min_length 256;

        # compress data for clients connecting via proxies
        gzip_proxied any;

        # directs proxies to cache both the regular and GZIP versions of an asset
        gzip_vary on;

        # disables GZIP compression for ancient browsers that don't support it
        gzip_disable "msie6";

        gzip_types text/css text/x-component application/x-javascript application/javascript text/javascript text/x-js text/richtext text/plain text/xsd text/xsl text/xml image/bmp application/java application/msword application/vnd.ms-fontobject application/x-msdownload image/x-icon application/json application/vnd.ms-access video/webm application/vnd.ms-project application/x-font-otf application/vnd.ms-opentype application/vnd.oasis.opendocument.database application/vnd.oasis.opendocument.chart application/vnd.oasis.opendocument.formula application/vnd.oasis.opendocument.graphics application/vnd.oasis.opendocument.spreadsheet application/vnd.oasis.opendocument.text audio/ogg application/pdf application/vnd.ms-powerpoint image/svg+xml application/x-shockwave-flash image/tiff application/x-font-ttf audio/wav application/vnd.ms-write application/font-woff application/font-woff2 application/vnd.ms-excel;
        location ~ \.(css|htc|less|js|js2|js3|js4)$ {
            expires 31536000s;
            etag on;
            if_modified_since exact;
            try_files $uri $uri/ /index.php?$args;
        }
        location ~ \.(html|htm|rtf|rtx|txt|xsd|xsl|xml)$ {
            etag on;
            if_modified_since exact;
            try_files $uri $uri/ /index.php?$args;
        }
        location ~ \.(asf|asx|wax|wmv|wmx|avi|avif|avifs|bmp|class|divx|doc|docx|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|webp|json|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|webm|mpp|_otf|odb|odc|odf|odg|odp|ods|odt|ogg|ogv|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|tif|tiff|_ttf|wav|wma|wri|xla|xls|xlsx|xlt|xlw|zip)$ {
            expires 31536000s;
            etag on;
            if_modified_since exact;
            try_files $uri $uri/ /index.php?$args;
        }
        add_header Referrer-Policy "no-referrer-when-downgrade";
        # END W3TC Browser Cache


}

콜론(:)을 입력합니다.
하단에 — INSERT — 대신 콜론이 표시되면 아래 명령어를 작성하고 엔터를 눌러서
example.com 을 자신의 도메인 주소 로 변경합니다.

:%s/example.com/도메인주소/g (엔터)

다섯개의 줄에서 일곱개의 변경이 일어났다는 메세지를 확인합니다.
변경을 마쳤으면 esc를 누르고 콜론+x 후에 엔터를 눌러 저장후 빠져나옵니다.
이번엔 웹서버 timeout 수정을 위해 nginx.conf 파일을 하나 더 생성합니다.

vim nginx.conf

a 를 눌러 — INSERT — 를 확인하고 아래 설정을 복사하여 붙여넣어줍니다.

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  600;
    proxy_connect_timeout 600;
    proxy_send_timeout 600;
    proxy_read_timeout 600;
    send_timeout 600s;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

esc 후에 콜론+x 를 눌러 저장하고 빠져나옵니다.
이번엔 인증서를 자동 갱신해주기 위해 스크립트를 작성합니다.

vim ssh_renew.sh

아래 내용을 복사하여, a를 눌러 하단 — INSERT — 를 확인하고 붙여넣기 합니다.

#!/bin/bash


COMPOSE="docker compose --ansi never"
DOCKER="/usr/bin/docker"


cd /home/ubuntu/wordpress/
$COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver

esc 누르고 콜론+x 를 눌러 저장하고 빠져나옵니다.
아래 명령어를 통해 파일이 실행 가능하도록 수정하고 주기적으로 실행하기 위해 crontab을 작성합니다.

chmod x ssl_renew.sh

crontab -e


아래와 같은 창이 나오면 2번을 눌르고 엔터를 눌러 vim을 선택해줍니다.

no crontab for root - using an empty one


Select an editor.  To change later, run 'select-editor'.
  1. /bin/nano        <---- easiest
  2. /usr/bin/vim.basic
  3. /usr/bin/vim.tiny
  4. /bin/ed


Choose 1-4 [1]:

이제 가장 하단으로 이동하여 a를 누르고 아래 내용을 복사하여 붙여넣어줍니다.

0 5 * * 1 /home/ubuntu/wordpress/ssl_renew.sh >> /home/ubuntu/wordpress/cron.log  2>&1

위와 같이 수정하면 매주 월요일 새벽 다섯시마다 인증서 갱신을 실행하게됩니다.
esc 를 누르고 콜론+x 하여 저장하고 빠져나옵니다.
이번엔 docker-compose.yml 파일에 위 내용을 적용하기 위해 파일을 수정합니다.
아래 명령어로 docker-compose.yml 파일을 삭제하고 새로 엽니다.

rm docker-compose.yml && vim docker-compose.yml

아래 설정을 복사하고, 편집기에서 a를 눌러 — INSERT — 를 확인하고 붙여넣어줍니다.

version: '3'


services:


  db:
    image: mariadb:latest
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - MYSQL_DATABASE=wordpress
    volumes:
      - ./dbdata:/var/lib/mysql
    networks:
      - app-network


  redis:
    image: 'redis:alpine'
    restart: unless-stopped
    networks:
      - app-network

  wordpress:
    depends_on:
      - db
    image: wordpress:fpm-alpine
    container_name: wordpress
    restart: unless-stopped
    env_file: .env
    environment:
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - ./wordpress:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/php.ini
    networks:
      - app-network


  webserver:
    depends_on:
      - wordpress
    image: nginx:alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./wordpress:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./certbot-etc:/etc/letsencrypt
    networks:
      - app-network


  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - ./certbot-etc:/etc/letsencrypt
      - ./wordpress:/var/www/html
    command: renew


volumes:
  certbot-etc:
  wordpress:
  dbdata:
  nginx-conf:


networks:
  app-network:
    driver: bridge

esc 를 누르고 콜론+x 를 눌러 저장하고 빠져나옵니다.
이제 https 를 적용한 wordpress 를 실행해줍니다.

docker compose up -d --force-recreate --no-deps webserver

정상 실행 되고나면 아래 명령어로 확인합니다.

docker compose ps

위와 같이 정상 실행되고나면 자신의 도메인 주소로 웹브라우저에서 접속합니다.

사용할 언어를 선택합니다. 저는 한국어를 선택했습니다. 이제 마음껏 워드프레스를 시작합니다!

요약

오라클 클라우드 워드프레스 구축 방법 을 자세하게 알아보았는데요. 오라클 클라우드에서 워드프레스를 실행하면 보안과 디스크, 성능 관점에서 다양한 이점을 누릴 수 있습니다. 이 문서에서는 오라클 클라우드에서 Docker를 설치하고 HTTPS 기반의 워드프레스를 생성하는 방법을 상세하게 정리했습니다. 내용은 길지만 실제로 따라하면 한시간 정도 소요되는 것 같네요. 궁금한 내용은 댓글을 달아주세요. 다음 글에서는 워드프레스의 테마와 플러그인을 설정해보겠습니다. 감사합니다.

참고자료

아래 링크의 글을 통해 직접 설치해보고, 다음글에서 소개드릴 plugin과 테마정보들을 반영하여 설정파일 일부를 수정했습니다.