소개
WordPress는 가장 인기 있는 콘텐츠 관리 시스템(CMS) 중 하나입니다. 통계적으로 전 세계 웹사이트의 39% 이상을 구동하고 있습니다. 플러그인을 통한 확장성과 유연한 템플릿 시스템 덕분에 인기가 높습니다. 단 몇 초 만에 외관을 변경할 수 있습니다. 또한, 많은 기술적 노하우 없이도 웹 인터페이스를 통해 관리할 수 있습니다.
또한, WordPress는 무료 오픈 소스이며 MySQL 데이터베이스와 PHP 처리를 기반으로 구축되었습니다. LAMP 스택(Linux, Apache, MySQL, PHP)에 WordPress를 배포하거나 LEMP 스택(Linux, Nginx, MySQL, PHP)에 배포할 수 있습니다. 하지만 배포할 때마다 스택을 설정하는 것은 시간이 많이 걸리는 일입니다.
다행히도 클라우드 컴퓨팅, Docker, 및 Docker Compose와 같은 현대적인 소프트웨어 제공 방식은 전반적인 개발자 경험을 원활하게 만들었습니다. 이러한 도구는 애플리케이션을 배포할 때마다 개별 구성 요소를 설치하고 구성하는 오버헤드를 피함으로써 모든 스택을 설정하는 프로세스를 단순화합니다. 대신, 이미지를 가져오고 생성하여 Docker 컨테이너에서 실행하는 데 사용할 구성 파일을 작성하므로 단 하나의 명령어로 애플리케이션을 배포할 수 있습니다.
컨테이너는 가볍고 가상화되었으며 이식성이 뛰어난 소프트웨어 정의 표준화된 환경으로, 물리적 호스트 머신에서 실행되는 다른 소프트웨어와 격리되어 소프트웨어가 실행될 수 있도록 합니다. Docker Compose를 사용하면 여러 컨테이너를 관리하고 이들이 서로 통신하도록 할 수 있습니다. 예를 들어, 애플리케이션 소스 코드와 데이터베이스는 서로 통신해야 합니다.
이 튜토리얼에서는 다중 컨테이너화된 WordPress 애플리케이션을 구축할 것입니다. 완전한 WordPress 앱에는 MySQL 데이터베이스, Nginx 서버, WordPress 소스 코드의 세 가지 컨테이너가 필요합니다. 현대 웹사이트에서 보안이 최우선 과제이므로, 설치를 보호하기 위해 Let’s Encrypt로부터 SSL 인증서를 획득할 것입니다. 그런 다음, 웹사이트의 보안이 지속적으로 유지되도록 인증서를 주기적으로 확인하고 갱신하는 cron 작업을 설정할 것입니다.
사전 요구 사항
- 이 튜토리얼은 실습 위주이므로 초기 운영 환경으로 Ubuntu 20.04가 설치되어 있어야 합니다. 또한 sudo 권한이 있는 non-root 사용자가 필요합니다. 다음은 Ubuntu 서버 설정을 도와주는 단계별 튜토리얼.
- 입니다. 또한 Docker를 설치해야 합니다. Ubuntu 18.04에 Docker를 설치하고 작동하는 방법.
- 에 대한 이 튜토리얼을 참조할 수 있습니다. Docker Compose 설치도 필요합니다. 튜토리얼Ubuntu 20.04에서 Docker Compose를 설치하고 구성하는 방법.
- 의 1단계를 따르시면 됩니다. Let’s Encrypt로부터 TLS/SSL 인증서를 받으려면 등록된 도메인 이름이 필요합니다. 이 튜토리얼에서는
example.com. - 을 사용하겠습니다. 트래픽이 VPS를 가리키도록 DNS 레코드를 설정하십시오. 두 개의 DNS 레코드가 필요합니다:
- 공인 IP 주소를 가리키는
example.com의 A 레코드. - 공인 IP 주소를 가리키는
www.example.com의 A 레코드.
- 공인 IP 주소를 가리키는
1단계: 웹 서버 구성 정의
웹 서버는 웹사이트 파일을 보관하고 사용자가 웹 애플리케이션에 액세스할 수 있도록 합니다. 따라서 첫 번째 단계에서 웹 서버 구성을 정의하는 것이 적절합니다. 여기서는 WordPress 전용 위치 블록을 포함하는 Nginx 서버 구성 파일을 정의할 것입니다. 또한 인증서 자동 갱신을 위해 Let’s Encrypt 검증 요청을 Certbot 클라이언트로 안내하는 위치 블록도 포함할 것입니다.
프로젝트를 위한 디렉토리를 만드는 것부터 시작해 보겠습니다. 원하는 디렉토리 이름을 선택할 수 있습니다. 이 튜토리얼에서는 wordpress_docker를 사용하겠습니다. 다음 명령어를 입력하여 디렉토리를 생성하고 해당 디렉토리로 이동합니다:
|
1 |
mkdir wordpress_docker && cd wordpress_docker |
다음으로, 아래 명령어를 사용하여 Nginx 구성 파일을 보관할 디렉토리를 생성합니다:
|
1 |
mkdir nginx-conf |
다음 명령어로 nano를 사용하여 파일을 엽니다:
|
1 |
nano nginx-conf/nginx.conf |
이 파일에서는 Nginx 서버 블록 구성을 위한 기본 지시문을 정의합니다. 여기에는 서버 이름, 문서 루트, 그리고 인증서에 대한 Certbot 플러그인 요청, 정적 파일 및 PHP 처리를 전달하기 위한 location 블록에 대한 지시문이 포함됩니다. 자세한 내용은 다음 튜토리얼을 참조하십시오: Let’s Encrypt로 Nginx를 보호하는 방법 파일에 다음 코드를 추가하고, example.com 부분을 등록한 도메인 이름으로 변경합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
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 app: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; } } |
추가한 섹션들을 정의해 보겠습니다:
-
지시문:
listen: Nginx가 포트에서 수신 대기하도록 지시합니다80. 이를 통해 Certbot의 webroot 플러그인을 사용하여 인증서 요청을 할 수 있습니다. SSL 인증서를 획득하면 이 구성을 업데이트하여 포트를 사용하도록 할 것입니다443.server_name: 이 지시문은 이 구성이 처리해야 할 도메인 이름을 정의합니다. 여기에 정의된 도메인 이름으로 들어오는 트래픽은 이 특정 서버 블록으로 전달되며, 따라서 문서root.root: 위의 도메인 이름에 대한 요청의 루트 디렉터리를 정의합니다. 일반적으로 실제 웹사이트 파일이 있는 디렉터리입니다. 디렉터리를 다음으로 설정했습니다:/var/www/html. 이것은 Docker 마운트 포인트로 생성될 것입니다 컨테이너 빌드 시간 동안. 이 프로세스에 대한 지침을 다음 내부에서 정의할 것입니다: WordPress Dockerfile.index: 이는 요청을 처리할 때 웹 서버의 인덱스 또는 진입점으로 사용할 파일을 정의합니다. Nginx가 우선적으로 처리하도록 index.html 앞에 index.php를 이동했습니다. 대상은index.php.
-
Location 블록:
location ~ /.well-known/acme-challenge: Certbot이 임시 파일을 추가하여 지정된 도메인의 DNS가 우리가 SSL 인증서를 요청하는 특정 서버를 가리키는지 확인하는 well-known 디렉터리에 대한 요청을 처리합니다. 이 단계가 작동하려면 이 튜토리얼에서 사용하는example.com대신 유효한 도메인을 추가해야 하는 이유가 바로 이 때문입니다.location /: URI 요청을 가져와 WordPressindex.php에 제어권을 넘겨 처리를 위한 인수를 요청합니다.location ~ \.php$: PHP 처리를 담당하고 요청을 WordPress 컨테이너로 전달합니다 (이에 대한 설정 파일은 이후 단계에서 정의할 것입니다). 여기서는 FastCGI 프로토콜에 특화된 설정을 정의했습니다. WordPress Docker 이미지가 php:fpm 이미지를 기반으로 하기 때문입니다. Nginx는 PHP 관련 요청을 위해 독립적인 PHP 프로세서를 사용합니다. 우리는php-fpm프로세서를 사용할 것입니다. 이 프로세서는php:fpmDocker 이미지와 함께 제공됩니다.location ~ /\.ht: Nginx가 사용하지 않는.htaccess파일을 처리합니다.deny all지시문은 이러한 파일이 웹사이트 방문자에게 절대 제공되지 않도록 보장합니다.location = /favicon.ico, location = /robots.txt: 정의에서 볼 수 있듯이, 이는/favicon.ico및/robots.txt파일에 대한 요청이 로깅되는 것을 방지합니다.location ~* \.(css|gif|ico|jpeg|jpg|js|png)$: 정적 파일에 대한 요청 로깅을 끄고, 서버 부하를 줄이기 위해 해당 파일들이 캐싱되도록 합니다.
이제 다음을 눌러 파일을 저장하고 닫을 수 있습니다: CTRL+X, Y, 그런 다음 ENTER. 이것으로 첫 번째 단계가 완료되었습니다.
Step 2: Define Environment Variables
환경 변수는 WordPress 애플리케이션과 데이터베이스 간의 통신을 원활하게 하는 데 필요합니다. 또한 애플리케이션 데이터가 유지되도록 보장합니다. 환경 변수에는 데이터베이스 자격 증명과 같은 민감한 정보와 데이터베이스 이름 및 호스트와 같은 민감하지 않은 정보가 포함됩니다.
보안을 위해 프로젝트 저장소에 민감한 정보를 추가하지 않는 것이 좋습니다. 따라서 Docker Compose 파일에 민감한 값을 설정하는 대신, 프로젝트 저장소에 커밋되어 공개적으로 노출될 위험이 없는 .env 파일 내에 MySQL 자격 증명을 정의할 것입니다. 프로젝트 root ~/wordpress_docker 내부에서 .env 파일을 엽니다:
|
1 |
nano .env |
|
1 2 3 |
MYSQL_ROOT_PASSWORD=your_strong_root_password MYSQL_USER=your_wordpress_database_user MYSQL_PASSWORD=strong_wordpress_database_password |
그 다음으로 해야 할 일은 .env 파일을 .gitignore 및 .dockerignore 파일에 추가하여 각각 저장소나 Docker 이미지에 추가되지 않도록 하는 것입니다.
이 튜토리얼에서는 필수 사항이 아니지만, 버전 관리를 위해 Git을 사용하려면 다음 명령어를 입력하여 현재 디렉터리를 git 저장소로 초기화하십시오:
|
1 |
git init |
nano로 .gitignore 파일을 엽니다:
|
1 |
nano .gitignore |
다음 줄을 추가합니다:
|
1 |
.env |
파일을 저장하고 닫습니다. 다음으로, nano로 .dockerignore 파일을 엽니다:
|
1 |
nano .dockerignore |
다음 줄을 추가합니다:
|
1 |
.env |
이 작업을 수행하는 동안, 애플리케이션 개발과 관련된 다른 파일 및 디렉터리를 선택적으로 추가할 수도 있습니다:
|
1 2 3 |
.env .git docker-compose.yml |
완료되면 파일을 저장하고 닫습니다. 이 단계는 이것으로 끝입니다. Docker Compose 정의로 넘어가겠습니다.
Step 3: Configure Services with Docker Compose
Docker Compose는 이미지를 빌드하기 위해 docker-compose.yml 파일을 사용합니다. 이 파일에는 애플리케이션의 전체 설정을 위한 서비스 정의가 포함되어 있습니다. 서비스 정의는 기본적으로 컨테이너가 실행되는 방식에 대한 지침입니다. 서비스는 실제로 실행 중인 컨테이너입니다.
Docker Compose를 사용하면 공유 네트워크 및 볼륨과 다양한 서비스를 함께 연결하여 다중 컨테이너 애플리케이션에 대해 서로 다른 서비스를 정의할 수 있습니다. 웹 서버, WordPress 설치 및 데이터베이스 등 애플리케이션을 위한 세 개의 컨테이너를 정의하면서 이를 실제로 확인하게 될 것입니다. 인증서 갱신을 위한 Certbot 클라이언트를 실행하기 위해 네 번째 컨테이너를 추가할 것입니다.
다음 명령어를 입력하여 docker-compose.yml 파일을 생성합니다:
|
1 |
nano docker-compose.yml |
The first line in a docker-compose.yml 파일의 첫 번째 줄은 버전 정의 줄입니다. 여기서는 3으로 설정했습니다. 그런 다음 서비스를 정의하기 시작할 수 있습니다. 파일에 다음 코드 스니펫을 추가하여 db 서비스를 정의합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
version: '3' services: #MySQL Service db: image: mysql:8.0 container_name: db restart: unless-stopped env_file: .env environment: - MYSQL_DATABASE=wordpress volumes: - dbdata:/var/lib/mysql command: '--default-authentication-plugin=mysql_native_password' networks: - app-network |
아래에서 db 서비스 정의에 포함된 내용을 살펴보겠습니다:
image: 컨테이너의 기반이 될 이미지를 결정합니다. 특정 버전(mysql:8.0)을 지정하는 것이 최신 태그(mysql:latest)를 사용하는 것보다 항상 더 좋습니다. 향후 버전의 MySQL 이미지가 이 이미지를 다시 빌드할 때 애플리케이션과 충돌할 수 있기 때문입니다. 공식 Dockerfile 문서의 Dockerfile 모범 사례.container_name: 여기에서 컨테이너 이름을 지정합니다.restart: 이 지시문은 컨테이너의 재시작 동작을 결정합니다. 기본값은no이지만, 수동으로 중지되지 않는 한 항상restart하도록 설정했습니다.env_file: 이 지시문은 애플리케이션에서 사용하는 환경 변수 파일(.env)의 위치를 지정하는 데 사용됩니다.environment: 추가 환경 변수를 지정하는 데 사용됩니다. 이 튜토리얼에서는 애플리케이션의 데이터베이스 이름을 저장하기 위해MYSQL_DATABASE변수를 지정했습니다. 데이터베이스 이름은docker-compose.yml에 포함될 수 있습니다..volumes: 마운트 위치를 지정하는 데 사용됩니다. 이 예제에서는 볼륨이라는 이름의 볼륨을 컨테이너의/var/lib/mysql디렉토리에 마운트했으며, 이는 일반적으로 MySQL의 표준 데이터 디렉토리입니다.command: 이 지시문은 이미지의 기본 CMD 명령을 재정의할 명령을 지정합니다. 컨테이너 내부에서 MySQL 서버를 시작하는 Docker 이미지의 표준mysqld명령에 옵션을 추가했습니다. 추가한 옵션은--default-authentication-plugin=mysql_native_password이며, 이는 MySQL의 기본 인증 플러그인을 비밀번호 인증(mysql_native_password)을 사용하도록 업데이트합니다. PHP(WordPress 애플리케이션이 작동하기 위해)는 데이터베이스에 액세스할 때 사용자 이름과 비밀번호를 사용하므로 이 설정이 필요합니다. 최신 MySQL 버전에서는 기본 인증 플러그인이 변경되었습니다. 그러나 대부분의 애플리케이션은 비밀번호 인증을 사용합니다. 따라서 애플리케이션이 작동하려면 이 설정을 변경해야 합니다.networks: 이 지시문은db서비스가app-network에 참여해야 함을 지정하는 데 사용되며, 이는 튜토리얼을 진행하면서 정의할 것입니다.
다음으로 WordPress 애플리케이션의 서비스 구성을 정의해 보겠습니다. 서비스와 container_name을 app(으)로 지정하겠습니다. 다음 코드 스니펫을 db 서비스 정의 아래에 추가하되, 들여쓰기를 올바르게 유지해야 합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#WordPress 애플리케이션 코드 서비스 app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app 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: - app:/var/www/html networks: - app-network |
Just like we did with the db 서비스에서 했던 것과 마찬가지로, 컨테이너 이름을 지정하고 재시작 정책을 정의했습니다. 우리가 추가한 몇 가지 옵션은 아래에 정의되어 있습니다:
depends_on: 이 지시문은 컨테이너가 의존성 순서대로 시작되도록 보장합니다. 우리의 경우,app컨테이너는db컨테이너에 의존합니다. 따라서db컨테이너가 시작된 후에 시작됩니다. WordPress 앱이 작동하려면 MySQL 데이터베이스를 사용할 수 있어야 하므로 이 순서대로 진행되어야 합니다.image: 코드 스니펫에서 볼 수 있듯이, 우리는 WordPress 버전 5.1.1 fpm alpine 이미지를 사용할 것입니다. Nginx가 PHP 처리를 위해 필요로 하는php-fpm프로세서에 대해 설명한 바 있습니다. 이 이미지가 그 역할을 담당합니다. Alpine Linux 프로젝트를 기반으로 한 alpine 이미지는 이미지 크기를 작게 유지하는 데 도움이 됩니다. 이미지 버전에 대한 자세한 정보가 필요한 경우, 다음 링크에서 Docker Hub WordPress 이미지.env_file: 데이터베이스 자격 증명이 포함된.env파일의 위치를 지정합니다.environment: 이 지시문은 추가 환경 변수를 정의합니다. 우리의 경우, WordPress가 예상하는 변수들을 정의하고 여기에.env파일의 변수 값을 할당합니다. 이 변수들은WORDPRESS_DB_USER,WORDPRESS_DB_PASSWORD, 그리고WORDPRESS_DB_HOST이며, 이는db컨테이너에서 실행 중인 MySQL 서버를 가리키며, MySQL의 기본 포트인3306번 포트를 통해 액세스할 수 있습니다. 마지막으로, WordPress로 설정한WORDPRESS_DB_NAME을 볼 수 있습니다. 동일한 값이 db 컨테이너의 MySQL 서비스 정의에도 지정되어 있습니다:MYSQL_DATABASE=wordpress.volumes: 이 지시문은 app이라는 볼륨을/var/www/html마운트 포인트에 마운트하며, 이는 WordPress 이미지에 의해 생성됩니다. 볼륨의 이름을 지정하면 다른 컨테이너와 애플리케이션 코드를 공유할 수 있습니다.networks: 마지막으로, app 컨테이너를app-network에 추가하여 네트워크상의 다른 컨테이너와 통신할 수 있도록 합니다.
이것으로 WordPress 이미지를 위한 app 서비스 컨테이너에 대한 설정은 끝났습니다. 이제 Nginx 이미지를 위한 webserver 서비스를 정의해 보겠습니다. 먼저, docker-compose.yml 파일의 app 서비스 정의 아래에 다음 코드 스니펫을 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#Webserver Nginx 서비스 webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network |
우리는 이미 depends_on 옵션에 대해 설명했습니다. 이 webserver 서비스의 경우, 컨테이너는 app 컨테이너가 시작된 후에 시작됩니다. 웹 서버 컨테이너는 alpine Nginx 이미지를 기반으로 합니다. 이전 서비스 정의와 유사한 재시작 정책을 가집니다. webserver 서비스 정의의 다른 옵션은 다음과 같습니다:
ports: 호스트 머신과 컨테이너 간의 포트를 바인딩합니다. 1단계에서 우리는 포트80를nginx.conf파일에 정의했습니다. 이 포트는 컨테이너의 포트80에 매핑됩니다.volumes: 이 옵션 아래에는 바인드 마운트와 명명된 볼륨의 조합이 있습니다:app:/var/www/html: 이 볼륨 정의는 WordPress 애플리케이션을 이전에 Nginx 서버 블록에서 루트로 설정했던/var/www/html디렉토리에 마운트합니다../nginx-conf:/etc/nginx/conf.d: 이 정의는 호스트 머신의 Nginx 설정 디렉토리를 컨테이너용으로 정의한 Nginx 설정 디렉토리에 바인드 마운트합니다. 따라서 호스트 머신에서의 모든 변경 사항이 컨테이너에 자동으로 반영됩니다.certbot-etc:/etc/letsencrypt: 이 정의는 도메인에 대한 Let’s Encrypt 인증서와 키를 컨테이너의 적절한 디렉토리에 마운트합니다.
networks: 이전 서비스 정의와 마찬가지로,networks지시어는 webserver 서비스를app-networks.
에 추가합니다. webserver 정의가 완료되었으므로 Certbot 서비스에 대한 지침을 추가해 보겠습니다. 이는 Let’s Encrypt로부터 TLS/SSL 인증서를 가져오는 작업을 처리합니다. Nginx 서버 보안에 대해 자세히 알고 싶다면, Let’s Encrypt로 Nginx를 보호하는 방법 튜토리얼이 좋은 참고 자료가 될 것입니다.
다음으로, webserver 서비스 아래에 다음 코드 스니펫을 추가합니다. 올바른 도메인 이름과 이메일 주소를 설정해야 합니다:
|
1 2 3 4 5 6 7 8 9 10 |
#certbot service certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com |
The certbot 이미지는 webserver가 시작된 후에만 시작됩니다. 이는 depends_on 지시어 때문입니다. Docker Compose는 정의된 대로 Docker Hub에서 Certbot 이미지를 가져옵니다.
volumes 정의에 따라 Certbot 컨테이너는 certbot-etc의 도메인 인증서 및 키를 Nginx webserver 컨테이너와 공유하고, 애플리케이션 코드를 app 컨테이너와 공유합니다.
Under the command 정의 아래에, 아래 나열된 추가 옵션과 함께 컨테이너의 기본 Certbot certonly 명령을 실행하도록 하위 명령을 지정했습니다.
-
--webroot: 인증을 위해 webroot 폴더에 파일을 배치하는 webroot 플러그인의 사용을 지정합니다.--webroot-path: webroot 디렉토리의 경로를 지정합니다.--agree-tos: 귀하가 ACME의 서비스 약관에 동의함을 지정합니다..--no-eff-email: 귀하의 이메일을 EFF와 공유하고 싶지 않음을 지정합니다. 공유를 원하시면 이 옵션을 생략할 수 있습니다.--staging: 실제 인증서를 받기 전에 구성을 테스트하기 위해 Let’s Encrypt 스테이징 환경에서 먼저 테스트 인증서를 받으려 함을 Certbot에 알립니다. Let’s Encrypt에는 도메인 요청 속도 제한이 있습니다. 따라서 구성을 먼저 테스트하면 도메인이 제한되는 것을 방지하는 데 도움이 됩니다.-d: 이 옵션은 인증서 요청을 위한 도메인 이름을 받습니다. 이 튜토리얼에서는example.com및www.example.com을 포함했습니다. 실제 등록된 도메인을 지정해 주세요.
우리의 docker-compose.yml 파일이 거의 완성되었습니다. 하지만 Certbot 서비스 아래에 네트워크 및 볼륨 정의도 추가해야 합니다:
|
1 2 3 4 5 6 7 8 9 10 |
#Volumes volumes: certbot-etc: app: dbdata: #Networks networks: app-network: driver: bridge |
The volumes 키는 이 compose 파일에 정의된 모든 서비스(컨테이너)와 공유할 볼륨을 정의합니다: certbot-etc, app, 그리고 dbdata. Docker가 생성하는 볼륨의 내용은 호스트 파일 시스템에서 Docker가 관리하는 디렉토리에 저장됩니다: /var/lib/docker/volumes/. 그런 다음 각 볼륨의 콘텐츠는 해당 볼륨을 사용하는 모든 컨테이너에 마운트됩니다. 이를 통해 컨테이너 간에 데이터와 코드를 공유할 수 있습니다.
이 networks 키는 컨테이너 간의 통신을 허용하는 브리지 네트워크를 정의합니다. 다음과 같이 동일한 브리지 네트워크에 있는 컨테이너는 webserver 및 db 외부 네트워크에 트래픽을 노출하지 않고 포트를 통해 안전하게 통신할 수 있습니다. 프론트엔드 웹사이트 페이지에 대한 액세스를 허용하기 위해 포트 80만 노출합니다.
완성된 docker-compose.yml 파일은 다음과 같습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
version: '3' services: #MySQL 서비스 db: image: mysql:8.0 container_name: db restart: unless-stopped env_file: .env environment: - MYSQL_DATABASE=wordpress volumes: - dbdata:/var/lib/mysql command: '--default-authentication-plugin=mysql_native_password' networks: - app-network #WordPress 애플리케이션 코드 서비스 app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app 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: - app:/var/www/html networks: - app-network #Webserver Nginx 서비스 webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network #certbot 서비스 certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com #볼륨 volumes: certbot-etc: app: dbdata: #네트워크 networks: app-network: driver: bridge |
파일을 저장하고 닫을 수 있습니다. 다음 단계에서는 컨테이너를 시작 및 테스트하고 인증서를 요청할 것입니다.
4단계: 컨테이너 실행 및 SSL 인증서 획득
Docker Compose의 가장 큰 장점은, 일단 모든 서비스를 docker-compose.yml 파일을 사용하면 단 하나의 명령어로 모든 컨테이너를 시작할 수 있습니다: docker-compose up. 이 명령어는 지정된 모든 지침을 실행합니다. 도메인 요청이 성공하면 터미널에서 올바른 종료 상태를 볼 수 있습니다. 컨테이너를 생성하려면 다음 명령어를 입력하세요. -d 플래그는 백그라운드에서 컨테이너를 실행하기 위한 것입니다:
|
1 |
docker-compose up -d |
아래 스크린샷과 같은 출력이 표시되면 서비스가 성공적으로 생성된 것입니다:
서비스 상태를 확인하려면 다음 명령어를 실행하세요: docker-compose ps 명령어:
|
1 |
docker-compose ps |
모든 것이 성공적이었다면 명령어의 출력은 아래와 같이 표시됩니다. app, db, 그리고 webserver 컨테이너의 상태는 up이어야 하며, certbot 컨테이너는 Exit0 상태여야 합니다:
만약 상태 열에 Up 이외의 다른 것이 표시되거나, app, db 또는 webserver, 혹은 Exit 상태가 0이 아닌 경우( certbot 컨테이너 기준), 무언가 잘못된 것입니다. 다음 명령어를 사용하여 각 컨테이너의 로그를 확인할 수 있습니다: docker-compose logs, 그리고 service_name:
|
1 |
docker-compose logs service_name |
예를 들어, 다음 명령어를 입력하여 certbot 컨테이너의 로그를 확인할 수 있습니다:
|
1 |
docker-compose logs certbot |
인증서가 webserver 컨테이너에 마운트되었는지 확인하려면 docker-compose exec 명령어를 사용하세요:
|
1 |
docker-compose exec webserver ls -la /etc/letsencrypt/live |
만약 example.com 이외의 실제 등록된 도메인 이름을 사용했고 인증서 요청이 성공했다면 다음과 유사한 출력이 표시됩니다:
인증서 요청이 성공한 것을 확인한 후, docker-compose.yml 파일을 편집하여 --staging 플래그를 제거할 수 있습니다. 다음 명령어로 파일을 엽니다: nano:
|
1 |
nano docker-compose.yml |
Certbot 서비스 정의 섹션으로 스크롤하여 command 옵션에서 --staging 플래그를 --force-renewal 플래그로 교체합니다. 이는 동일한 도메인의 인증서에 대해 인증서 갱신을 요청하고 있음을 Certbot에 알립니다. 이제 Certbot 서비스 정의는 다음과 같아야 합니다:
|
1 2 3 4 5 6 7 8 9 10 |
#certbot service certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com |
편집을 마쳤으면 파일을 저장합니다.
다음 명령어를 입력하여 certbot 컨테이너를 다시 생성합니다. 여기에 포함된 --no-deps 플래그는 웹 서버 서비스가 이미 실행 중이므로 다시 시작하지 않고 건너뛰도록 Compose에 지시합니다:
|
1 |
docker-compose up --force-recreate --no-deps Certbot |
명령어는 인증서 요청이 성공했음을 보여주는 다음 스크린샷을 출력합니다:
이번 단계는 여기까지입니다. 다음 단계에서는 SSL 인증서를 포함하도록 Nginx 설정 파일을 수정합니다.
5단계: Nginx 설정 및 서비스 정의에서 SSL 활성화하기
Nginx가 보안 SSL을 통해 트래픽을 서비스하도록 하려면, 먼저 Nginx 설정 파일을 수정하여 HTTP에서 HTTPS로의 리디렉션을 추가해야 합니다. 그런 다음 인증서 및 키 위치를 지정하고, 마지막으로 보안 매개변수와 헤더를 추가해야 합니다.
설정 파일을 수정하기 전에, 다음 명령어로 recommended Nginx security parameters를 Certbot의 GitHub 저장소에서 curl을 사용하여 가져와야 합니다:
|
1 |
curl -sSLo nginx-conf/options-ssl-nginx.conf |
이 명령은 실행되어 가져온 매개변수를 options-ssl-nginx.conf 파일에 저장하며, 이 파일은 nginx-conf 디렉터리 내에 있습니다. 다음 명령을 사용하여 새 Nginx 구성 파일을 만들 수 있도록 기존 Nginx 구성 파일을 제거합니다:
|
1 2 |
rm nginx-conf/nginx.conf nano nginx-conf/nginx.conf |
이제 비어 있는 nginx.conf 파일에 다음 코드를 추가합니다. 이 코드에는 HTTP에서 HTTPS, SSL 인증 프로토콜로의 리디렉션 및 보안 헤더가 포함되어 있습니다. 이전에 했던 것처럼 example.com 도메인을 등록된 본인의 도메인으로 바꾸십시오:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
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; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.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-보안-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_pass app: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; } } |
포트를 사용하여 보안되지 않은 요청을 처리하는 첫 번째 서버 블록에서는 80번 포트를 지정하고, Certbot 갱신 요청을 위한 웹 루트를 지정합니다. 또한 다음과 같은 리디렉션 지시문을 포함하여 HTTP 요청을 HTTPS로 리디렉션합니다..
두 번째 서버 블록은 HTTPS 보안 트래픽이 들어오는 443번 포트를 처리합니다. 보시다시피 SSL 및 HTTP2. HTTP/2는 서버의 성능을 향상시킵니다. 이에 대한 자세한 내용은 HTTP/2에 대한 공식 Nginx 문서에서 더 읽어볼 수 있습니다..
이 블록에서는 Nginx가 SSL 인증서 및 키 위치를 포함하도록 지정했으며, curl이 nginx-conf/options-ssl-nginx.conf 디렉터리에 저장한 권장 Certbot 보안 매개변수도 포함하도록 지정했습니다.
추가 보안 헤더는 다음과 같은 보안 테스트 사이트에서 웹사이트의 등급을 높이는 역할을 합니다: Security Headers 및 SSL Labs. 자세한 내용을 알아보려면 이 헤더의 링크를 따르십시오: X-Frame-Options, Referrer Policy, X-Content-Type-Options, X-XSS-Protection, Content-Security-Policy. 저희는 HTTP Strict Transport Security (HSTS) 헤더를 주석 처리했습니다. 이 헤더의 프리로드(preload) 기능에 대해 읽어보고 활성화 여부를 자유롭게 결정할 수 있습니다.
다음과 같은 나머지 지시문은 root, index, 및 WordPress 전용 location 블록은 1단계에서 논의한 대로 유지됩니다. 편집을 마쳤으면 이제 파일을 저장하고 닫을 수 있습니다.
이제 HTTPS 트래픽이 사용하는 443,번 포트를 웹 서버의 서비스 정의에서도 활성화해야 합니다. 다음 명령을 입력하여 docker-compose.yml 파일을 nano로 엽니다:
|
1 |
nano docker-compose.yml |
ports 옵션 아래의 webserver 섹션에 443번 포트에 대한 매핑을 아래와 같이 강조 표시된 대로 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" - "443:443" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network |
완성된 docker-compose.yml 파일은 다음과 같아야 합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
version: '3' services: #MySQL 서비스 db: image: mysql:8.0 container_name: db restart: unless-stopped env_file: .env environment: - MYSQL_DATABASE=wordpress volumes: - dbdata:/var/lib/mysql command: '--default-authentication-plugin=mysql_native_password' networks: - app-network #WordPress 애플리케이션 코드 서비스 app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app 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: - app:/var/www/html networks: - app-network #웹서버 Nginx 서비스 webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" - "443:443" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network #certbot 서비스 certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com #볼륨 volumes: certbot-etc: app: dbdata: #네트워크 networks: app-network: driver: bridge |
모든 것이 올바르게 보이는지 확인한 후, 파일을 저장하고 닫습니다. 그 후, 다음 명령을 실행하여 webserver 서비스를 재생성합니다:
|
1 |
docker-compose up -d --force-recreate --no-deps webserver |
|
1 |
docker-compose ps |
이제 모든 컨테이너가 실행 중이므로 웹 인터페이스에서 WordPress 설정을 진행할 수 있습니다.
6단계: 웹 인터페이스에서 WordPress 설정 완료하기
설치를 계속하려면 서버의 도메인 이름으로 이동하십시오. 워드프레스 설치 홈 페이지가 표시될 것입니다. 계속하기 전에 언어를 선택하라는 환영 메시지가 나타납니다:
언어를 선택하고 계속을 클릭하여 다음 페이지로 이동합니다:
이 페이지에서 웹사이트 제목을 입력하고, 기억하기 쉬운 사용자 이름과 강력한 비밀번호를 선택하십시오. 보안상의 이유로 Admin을 사용자 이름으로 사용하지 않는 것이 좋습니다. 이메일을 입력하고 WordPress 설치 버튼을 클릭하여 워드프레스 설치를 시작하십시오.
설치가 완료되면 설정한 사용자 이름과 비밀번호를 입력하는 로그인 화면으로 이동합니다. 올바른 자격 증명을 입력하면 워드프레스 대시보드를 볼 수 있습니다:
이제 워드프레스를 성공적으로 설치했습니다! 다음으로, SSL 인증서가 자동으로 갱신되도록 단계를 진행해야 합니다.
7단계: 자동 SSL 인증서 갱신 구성
Let’s Encrypt TLS/SSL 인증서는 90일 동안만 유효합니다. 만료되지 않도록 자동 갱신 구성을 만드는 것은 사용자의 몫입니다. 스크립트를 생성하고 이를 크론 작업 유틸리티로 예약하여 이를 달성할 수 있습니다. 이 단계에서는 인증서를 갱신하는 스크립트를 만드는 방법을 보여줍니다. 그런 다음 크론 작업 유틸리티로 예약하여 주기적으로 실행하고 만료 날짜가 다가오면 인증서를 갱신하도록 합니다.
다음 wordpress_docker 프로젝트 디렉터리에서, ssl_renewer.sh 라는 이름의 스크립트를 nano로 엽니다.:
|
1 |
nano ssl_renewer.sh |
자동 갱신 및 Nginx 구성 재로드를 처리하기 위해 스크립트에 다음 코드를 추가합니다. 강조 표시된 사용자 이름을 루트가 아닌 사용자 이름으로 바꾸어야 합니다:
|
1 2 3 4 5 6 7 8 |
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose –ansi never" DOCKER="/usr/bin/docker" cd /home/hackins/wordpress_docker/ $COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af |
이 스크립트에서는 docker-compose 바이너리를 COMPOSE 변수에 할당합니다. 또한 스크립트가 –ansi never 옵션을 포함하여 docker-compose 명령을 ANSI 제어 문자 없이 실행하도록 지시합니다. 또한 Docker 바이너리를 DOCKER.
변수에 할당합니다. 그런 다음 스크립트는 프로젝트 디렉터리인 wordpress_docker로 이동하여 다음 명령을 실행합니다:
docker-compose run: certbot 컨테이너를 시작하고 certbot 서비스 정의에 제공한 명령을 재정의합니다. certonly 하위 명령을 실행하는 대신, 만료 예정인 Let’s Encrypt의 SSL/TLS 인증서를 갱신하는 renew 하위 명령을 실행합니다.docker-compose kill: SIGHUP 신호를webserver컨테이너에 보내 Nginx 구성을 재로드합니다. Docker의 이 튜토리얼에서 공식 Nginx Docker 이미지를 사용하는 방법을 확인해 보시기 바랍니다..docker system prune: 이 명령은 사용되지 않는 모든 컨테이너와 이미지를 제거합니다.
편집을 마치면 파일을 저장하고 닫습니다. 그런 다음, 다음 명령을 실행하여 실행 가능하게 만듭니다:
|
1 |
chmod +x ssl_renewer.sh |
실행 가능하게 만들었으면, 루트 crontab 파일을 열어 지정할 간격으로 스크립트를 주기적으로 실행합니다:
|
1 |
sudo crontab -e |
The crontab을 처음 사용하는 경우 선호하는 편집기를 선택하라는 메시지가 표시됩니다:
선호하는 편집기를 선택하고 Enter 키를 눌러 파일을 엽니다. 파일 맨 아래에 다음 줄을 추가합니다:
|
1 |
*/5 * * * * /home/hackins/wordpress_docker/ssl_renewer.sh >> /var/log/cron_docker.log 2>&1 |
이렇게 하면 갱신 스크립트가 작동하는지 여부를 테스트할 수 있도록 간격이 5분으로 설정됩니다. 또한 작업의 출력을 저장할 로그 파일도 지정했습니다: cron_docker.log.
5분 동안 기다린 후 cron.log 스크립트가 갱신 요청에 성공했는지 확인하려면:
|
1 |
tail -f /var/log/cron_docker.log |
요청이 성공했다면 아래 스크린샷과 유사한 내용이 표시될 것입니다:
이제 테스트를 거쳐 작동하는 것을 확인했으므로, crontab 파일을 수정하여 매일 갱신하도록 지정할 수 있습니다. 예를 들어, 매일 오후 6시에 스크립트가 실행되도록 지정하고 싶을 수 있습니다. 그렇게 하려면 crontab의 마지막 줄을 다음과 같이 수정하십시오:
|
1 |
0 18 * * * /home/hackins/wordpress_docker/ssl_renewer.sh >> /var/log/cron_docker.log 2>&1 |
또한, –dry-run 플래그를 ssl_renewer.sh 스크립트에서 제거하여 실행 시 실제 갱신이 이루어지도록 해야 합니다. 다음과 같이 보여야 합니다:
|
1 2 3 4 5 6 7 8 |
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose --ansi never" DOCKER="/usr/bin/docker" cd /home/hackins/wordpress_docker/ $COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af |
다음으로 파일을 저장하고 닫습니다. 이 작업을 마치면 크론 작업이 90일이 끝나기 전에 인증서를 갱신하여 유효하게 유지해 줍니다.
결론
이 튜토리얼을 여기까지 따라오셨다면, DevOps 엔지니어에 한 걸음 더 가까워졌다고 생각하셔도 좋습니다. 여러분은 Nginx 설정 스크립트를 작성하고, docker-compose.yml 파일을 생성했으며, Docker 및 Docker Compose로 WordPress 애플리케이션을 실행하는 데 필요한 여러 서비스를 정의했습니다. 웹 서버의 보안을 보장하기 위해 Let’s Encrypt로부터 SSL/TLS 인증서를 획득했습니다. 마지막으로, 인증서가 만료되지 않도록 크론 작업을 생성했습니다. 수고하셨습니다!
DevOps에 대해 더 깊이 알아보고 싶다면, 다음에서 컨테이너에 관한 더 많은 리소스를 확인해 보세요: 저희 블로그:
- Kubernetes 알아보기
- Ubuntu 20.04에서 Docker를 사용하여 Node.js (Express.js) 앱을 배포하는 방법
- Ubuntu 18.04에서 Kubernetes 클러스터에 PHP 애플리케이션 배포하기
- Docker Compose를 사용하여 Laravel, Nginx 및 MySQL 배포하기
즐거운 컴퓨팅 되세요!










댓글
아직 댓글이 없습니다. 첫 번째로 작성해 보세요.