يك سيستم مديريت محتوا (CMS) است كه به زبان PHP نوشته شده و تحت مجوز عمومي منبع آزاد GNU توزيع مي شود. مردم و سازمانهاي مختلف در سراسر جهان از Drupal براي ايجاد سايتهاي دولتي ، وبلاگ هاي شخصي ، كسب و كارها و موارد ديگر استفاده مي كنند. آنچه Drupal را از ساير چارچوبهاي CMS منحصر به فرد مي كند ، جامعه در حال رشد آن و مجموعه اي از ويژگي هايي است كه شامل فرآيندهاي ايمن ، عملكرد قابل اعتماد ، مدولاريتي (پيمانه اي بودن) و انعطاف پذيري در انطباق است.
Drupal نياز به نصب پشته LAMP (Linux ، Apache ، MySQLيا PHP) يا پشته LEMP (Linux، Nginx ، MySQL و PHP) دارد ، اما نصب تك تك مؤلفه ها يك كار زمان بر است. ما مي توانيم از ابزارهايي مانند Docker و Docker Compose براي ساده كردن روند نصب Drupal استفاده كنيم. در اين آموزش از تصاوير Docker براي نصب مولفه هاي جداگانه در كانتينر Docker استفاده خواهد شد. با استفاده از Docker Compose مي توان چندين كانتينر را براي پايگاه داده ، برنامه و شبكه / ارتباط بين آنها تعريف و مديريت كرد.
در اين آموزش Drupal را با استفاده از Docker Compose نصب خواهيم كرد تا بتوانيم از كانتينرينگ استفاده كرده و وب سايت Drupal خود را روي سرورها مستقر كنيم. كانتينرهايي براي يك پايگاه داده MySQL ، وب سرور مجازي Nginx و Drupal اجرا خواهيم كرد. همچنين با بدست آوردن گواهينامه هاي TLS / SSL با Let’s Encrypt براي دامنه مورد نظر جهت پيوند با سايت خود ، نصب خود را ايمن خواهيم كرد. سرانجام ، يك فرآيند cron را براي تمديد گواهينامه هاي خود تنظيم خواهيم كرد تا دامنه مان ايمن بماند.
پيش نيازها
براي دنبال كردن اين آموزش ، به موارد زير نياز خواهيم داشت:
⦁ سروري كه اوبونتو 18.04 را اجرا مي كند ، به همراه يك كاربر غير ريشه با امتيازات sudo و يك فايروال فعال. براي راهنمايي در مورد نحوه تنظيم اين موارد ، لطفاً به اين راهنماي تنظيم اوليه سرور مجازي مراجعه كنيد.
⦁ Docker كه طبق مراحل 1 و 2 نحوه نصب و استفاده از Docker در اوبونتو 18.04 بر روي سرور مجازي نصب شده باشد. اين آموزش بر روي نسخه 19.03.8 تست شده است.
⦁ Docker Compose كه طبق مرحله 1 نحوه نصب Docker در اوبونتو 18.04 بر روي سرور مجازي نصب باشد. اين آموزش بر روي نسخه 1.21.2 تست شده است.
⦁ نام دامنه ثبت شده. در سراسر اين آموزش از your_domain استفاده خواهد كرد. مي توانيد يك نام دامنه به صورت رايگان در Freenom دريافت كنيد ، و يا از ثبت كننده دامنه مورد نظر خود استفاده كنيد.
⦁ هر دو فايل DNS زير براي سرور مجازي شما تنظيم شده باشد.
⦁ يك ركورد A با your_domain كه به آدرس IP عمومي سرور مجازي شما اشاره كند.
⦁ ركورد A با www.your_domain كه به آدرس IP عمومي سرور مجازي شما نشان مي دهد.
مرحله 1 – تعريف پيكربندي وب سرور
قبل از اجراي هر نوع كانتينر ، بايد پيكربندي سرور مجازي وب Nginx خود را تعريف كنيم. فايل پيكربندي ما شامل برخي از بلوك هاي مكاني مخصوص Drupal ، به همراه بلوك موقعيت مكاني براي هدايت درخواست هاي تأييد Let’s Encrypt به كلاينت Certbot براي تمديد خودكار گواهينامه ميباشد.
ابتدا ، اجازه دهيد يك دايركتوري پروژه براي مجموعه Drupal خود به نام drupal ايجاد كنيم:
⦁ $ mkdir drupal

به ديركتوري جديد ايجاد شده برويد:
⦁ $ cd drupal

اكنون مي توانيم يك دايركتوري براي فايل پيكربندي خود تهيه كنيم:
⦁ $ mkdir nginx-conf

فايل را با nano يا ويرايشگر متن مورد علاقه خود باز كنيد:
⦁ $ nano nginx-conf/nginx.conf

در اين فايل ، يك بلوك سرور مجازي با دستورالعمل هايي براي نام سرور مجازي و ريشه اسناد ، و بلوك هاي مكان براي هدايت درخواست كلاينت Certbot براي صدور گواهينامه ها ، پردازش PHP و درخواست هاي دارايي استاتيك اضافه خواهيم كرد.
كد زير را در فايل اضافه كنيد. حتماً your_domain را با نام دامنه خود جايگزين كنيد:
~/drupal/nginx-conf/nginx.conf
server {
listen 80;
listen [::]:80;

server_name your_domain www.your_domain;

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;
}

rewrite ^/core/authorize.php/core/authorize.php(.*)$ /core/authorize.php$1;

location ~ .php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass drupal: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 گوش دهد ، كه به ما اين امكان را مي دهد تا از پلاگين webroot Certbot براي درخواست هاي گواهي خود استفاده كنيم. توجه داشته باشيد كه ما هنوز پورت 443 را دراختيار نداريم – پس از اينكه گواهينامه هاي خود را با موفقيت به دست آورديم ، پيكربندي خود را براي شامل شدن SSL به روز خواهيم كرد.
⦁ server_name: نام سرور مجازي ما و بلوك سرور مجازي را كه بايد براي درخواست به سرور مجازي ما استفاده شود ، تعريف مي كند. حتما your_domain را در اين خط با نام دامنه خود جايگزين كنيد.
⦁ index: فايل هايي را كه هنگام پردازش درخواست به سرور مجازي ما به عنوان ديركتوري استفاده مي شوند ، تعريف مي كند. ما ترتيب پيش فرض اولويت را در اينجا تغيير داده ايم ، index.php را در جلوي index.html قرار مي دهيم تا Nginx در صورت امكان فايلهايي به نام index.php را در اولويت قرار دهد.
⦁ Root: دستورالعمل root ديركتوري اصلي براي درخواست به سرور مجازي ما را نامگذاري مي كند. اين ديركتوري ، / var / www / html ، به عنوان يك نقطه نصب در زمان ساخت با دستورالعمل هايي در Drupal Dockerfile ما ايجاد مي شود. اين دستورالعمل Dockerfile همچنين اطمينان حاصل مي كند كه فايل هاي موجود در نسخه Drupal روي اين حجم نصب شده اند.
⦁ rewrite: اگر عبارت معمول و مشخص شده (^/core/authorize.php/core/authorize.php(.*)$) با يك URI درخواستي مطابقت داشته باشد ، URI همانطور كه در رشته جايگزيني مشخص شده است تغيير مي كند (/core/authorize.php$1)
بلوك هاي موقعيت مكاني:
⦁ location ~ /.well-known/acme-challenge : اين بلوك موقعيت مكاني درخواست ها به دايركتوري .well-known را مديريت ميكند كه در آن Certbot يك فايل موقت قرار مي دهد تا تأييد كند كه DNS براي دامنه ما روي سرور مجازي مناسب ميباشد. با استقرار اين پيكربندي ، مي توانيم از پلاگين webroot Certbot براي به دست آوردن گواهينامه هاي دامنه خود استفاده كنيم.
⦁ location / : در اين بلوك موقعيت مكاني ، ما از يك دستورالعمل try_files براي بررسي فايل هاي مطابق با درخواست هاي URI استفاده خواهيم كرد. با اين وجود ، به جاي بازگشت يك وضعيت 404 Not Found به عنوان پيش فرض ، با آرگومان هاي درخواست ، كنترل را به فايل index.php Drupal خواهيم داد.
⦁ location ~ .php$ : اين بخش موقعيت مكاني پردازش PHP و پروكسي اين درخواستها به كانتينر Drupal را مديريت مي كند. از آنجا كه تصوير Drupal Docker ما مبتني بر تصوير php: fpm خواهد بود ، همچنين گزينه هاي پيكربندي خاص را در پروتكل FastCGI در اين بلوك قرار خواهيم داد. Nginx براي درخواست هاي PHP به يك پردازنده مستقل PHP احتياج دارد: در مورد ما ، اين درخواست ها توسط پردازنده php-fpm كه همراه با تصوير php: fpm است ، انجام مي شود. علاوه بر اين ، اين بلوك موقعيت مكاني شامل دستورالعمل ها ، متغيرها و گزينه هاي خاص FastCGI است كه درخواست ها به برنامه Drupal را كه در كانتينر Drupal ما اجرا مي شود ، پروكسي ميكند، و شاخص مورد نظر را براي URI درخواستي تجزيه شده و درخواست هاي URI تنظيم ميكند.
⦁ location ~ /.ht : اين بلوك فايل هاي html دسترسي را از آنجايي كه Nginx به آنها سرويس نمي دهد ، مديريت خواهد كرد. دستورالعمل deny_all تضمين مي كند كه فايل هاي .htaccess هرگز در اختيار كاربران قرار نمي گيرد.
⦁ location = /favicon.ico, location = /robots.txt : اين بلوك ها اطمينان حاصل مي كنند كه درخواست ها به /favicon.ico و /robots.txt وارد نشوند.
⦁ location ~* .(css|gif|ico|jpeg|jpg|js|png)$ : اين بلوك ورود به سيستم براي درخواست هاي دارايي استاتيك را غيرفعال مي كند و تضمين مي كند كه اين دارايي ها بسيار قابل ذخيره هستند ، زيرا ارائه آن ها معمولاً گران است.
براي كسب اطلاعات بيشتر در مورد پراكسي FastCGI ، به راهنماي درك و اجراي غير مجاز مي باشدing FastCGI در Nginx مراجعه كنيد. براي كسب اطلاعات در مورد بلوك هاي سرور و موقعيت مكاني ، به راهنماي درك سرور Nginx و الگوريتم هاي انتخاب بلوك موقعيت مراجعه كنيد.
پس از پايان ويرايش ، فايل را ذخيره كنيد و ببنديد.
با تنظيم Nginx در محل خود ، مي توانيد به سراغ ايجاد متغيرهاي محيط برويد تا در زمان اجرا به كانتينرها برنامه و پايگاه داده خود منتقل شويد.
مرحله 2 – تعيين متغيرهاي محيط
برنامه Drupal ما براي ذخيره اطلاعات مربوط به سايت به يك پايگاه داده (MySQL ، PostgresSQL و غيره) نياز دارد. براي دسترسي به كانتينر پايگاه داده (MySQL) ، كانتينر Drupal در زمان اجرا به برخي از متغيرهاي محيطي نياز دارد. اين متغيرها حاوي اطلاعات حساسي مانند اعتبارات پايگاه داده هستند ، بنابراين نمي توانيم مستقيماً آنها را در فايل Docker Compose قرار دهيم – فايل اصلي كه حاوي اطلاعاتي در مورد نحوه اجراي كانتينرهاي ما است.
هميشه توصيه مي شود مقادير حساس را در فايل .env تنظيم كنيد و گردش آن را محدود كنيد. اين كار مانع از كپي شدن اين مقادير در مخازن پروژه مي شود و در معرض ديد عموم قرار نميگيرد.
در ديركتوري اصلي پروژه ، ~ / drupal ، فايلي با نام .env ايجاد و آن را باز كنيد:
⦁ $ nano .env

متغيرهاي زير را به فايل .env اضافه كنيد و بخش هايلايت شده را با اعتبار مورد نظر خود جايگزين كنيد:
~/drupal/.env
MYSQL_ROOT_PASSWORD=root_password
MYSQL_DATABASE=drupal
MYSQL_USER=drupal_database_user
MYSQL_PASSWORD=drupal_database_password

اكنون گذرواژه را براي حساب ريشه MySQL و همچنين نام كاربري و رمزعبور مورد نظر خود براي بانك اطلاعات برنامه خود اضافه كرده ايم.
فايل .env ما حاوي اطلاعات حساسي است ، بنابراين هميشه توصيه مي شود آن را در فايل هاي .gitignore و .dockerignore يك پروژه قرار دهيد تا به مخازن Git و تصاوير Docker اضافه نشود.
اگر مي خواهيد براي كنترل نسخه با Git كار كنيد ، ديركتوري كاري فعلي خود را به عنوان يك مخزن با git init مقداردهي كنيد:
⦁ $ git init

فايل gitignore را باز كنيد:
⦁ $ nano .gitignore

موارد زير را اضافه كنيد:
~/drupal/.gitignore
.env
فايل را ذخيره كنيد و از آن خارج شويد.
به همين ترتيب ، فايل .dockerignore را باز كنيد:
⦁ $ nano .dockerignore

سپس موارد زير را اضافه كنيد:
~/drupal/.dockerignore
.env
.git
فايل را ذخيره كنيد و از آن خارج شويد.
اكنون كه براي حفظ اعتبار خود به عنوان متغيرهاي محيطي اقداماتي انجام داده ايم ، بياييد به مرحله بعدي تعريف خدمات خود در فايل docker-compose.yml برويم.
مرحله 3 – تعريف خدمات با Compose Docker
Docker Compose ابزاري براي تعريف و اجراي برنامه هاي چند كانتينري Docker است. ما براي پيكربندي خدمات برنامه خود ، يك فايل YAML تعريف مي كنيم. سرويس در Docker Compose يك كانتينر در حال اجرا است و Compose به ما اجازه مي دهد تا اين سرويس ها را با حجم و شبكه هاي مشترك پيوند دهيم.
كانتينرهاي مختلفي را براي برنامه Drupal ، پايگاه داده و سرور مجازي وب خود ايجاد خواهيم كرد. در كنار اينها ، همچنين يك كانتينر براي اجراي Certbot ايجاد خواهيم كرد تا بتوانيم گواهينامه هايي را براي سرور مجازي وب خود بدست آوريم.
يك فايل docker-compose.yml ايجاد كنيد:
⦁ $ nano docker-compose.yml

براي تعريف نسخه فايل Compose و سرويس پايگاه داده mysql كد زير را اضافه كنيد:
~/drupal/docker-compose.yml
version: “3”

services:
mysql:
image: mysql:8.0
container_name: mysql
command: –default-authentication-plugin=mysql_native_password
restart: unless-stopped
env_file: .env
volumes:
– db-data:/var/lib/mysql
networks:
– internal

بياييد همه گزينه هاي پيكربندي سرويس mysql را يك به يك مرور كنيم:
⦁ image: تصويري را كه براي ايجاد كانتينر استفاده يا واكشي خواهد شد، مشخص مي كند. هميشه براي جلوگيري از مشكلات بعدي توصيه مي شود از تصوير با برچسب نسخه مناسب به استثناي آخرين برچسب استفاده كنيد. اطلاعات بيشتر در مورد بهترين روش هاي Dockerfile را از اسناد Docker بخوانيد.
⦁ container_name:براي تعريف نام كانتينر.
⦁ Command: از اين گزينه براي رونويسي دستور پيش فرض (دستورالعمل CMD) در تصوير استفاده مي شود. MySQL از افزونه هاي مختلف تأييد اعتبار پشتيباني كرده است ، اما mysql_native_password m روش معمول تأييد اعتبار است. از آنجا كه PHP ، و از اين رو Drupal ، از تأييد هويت MySQL جديدتر پشتيباني نمي كنند ، ما بايد –default-authentication-plugin=mysql_native_password  را به عنوان مكانيزم پيش فرض تأييد اعتبار تنظيم كنيم.
⦁ restart: براي تعريف رويكرد ريستارت كانتينر استفاده مي شود. رويكرد unless-stopped يك كانتينر را مجدداً راه اندازي مي كند مگر اينكه به صورت دستي متوقف شود.
⦁ env_file: متغيرهاي محيط را از يك فايل اضافه مي كند. در مورد ما متغيرهاي محيط را از فايل .env تعريف شده در مرحله قبل مي خواند.
⦁ volumes: مسيرهاي هاست يا واليوم هاي نامگذاري را به عنوان گزينه هايي براي يك سرويس مشخص مي كند. ما يك واليوم نامگذاري شده به نام db-data را در ديركتوري / var / lib / mysql روي كانتينر نصب مي كنيم ، جايي كه MySQL بصورت پيش فرض فايل هاي داده خود را خواهد نوشت.
⦁ networks: شبكه داخلي را كه سرويس برنامه ما به آن ملحق مي شود ، تعريف مي كند. ما در انتهاي فايل شبكه ها را تعريف خواهيم كرد.
تعريف سرويس mysql ما را انجام داده ايم ، بنابراين اكنون بياييد تعريف سرويس برنامه Drupal را به انتهاي فايل اضافه كنيم:
~/drupal/docker-compose.yml

drupal:
image: drupal:8.7.8-fpm-alpine
container_name: drupal
depends_on:
– mysql
restart: unless-stopped
networks:
– internal
– external
volumes:
– drupal-data:/var/www/html

در اين تعريف سرويس ، ما همانطور كه با سرويس mysql انجام داديم ، كانتينر خود را نامگذاري مي كنيم و يك سياست راه اندازي مجدد را تعريف مي كنيم. ما همچنين گزينه هاي خاصي را براي اين كانتينر اضافه مي كنيم:
Image: در اينجا ، از تصوير 8.7.8-fpm-alpine استفاده مي كنيم. اين تصوير داراي پردازنده php-fpm است كه سرور مجازي وب Nginx ما براي پردازش PHP نياز دارد. علاوه بر اين ، از تصوير alpine ، مشتق از پروژه Alpine Linux استفاده مي كنيم ، كه باعث كاهش سايز تصوير كلي مي شود و در بهترين روش هاي Dockerfile توصيه مي شود. Drupal نسخه هاي بيشتري از تصاوير دارد ، بنابراين آنها را در Dockerhub بررسي كنيد.
depends_on : براي بيان وابستگي بين خدمات استفاده مي شود. تعيين سرويس mysql به عنوان وابستگي به كانتينر Drupal ما ، اطمينان حاصل مي كند كه كانتينر Drupal ما پس از كانتينر mysql ايجاد مي شود و برنامه ما را قادر مي سازد تا يكنواخت شروع شود.
networks: در اينجا ، ما اين كانتينر را به همراه شبكه داخلي به شبكه خارجي اضافه كرده ايم. اين اطمينان حاصل مي كند كه سرويس mysql ما فقط از طريق كانتينر داخلي Drupal از طريق شبكه داخلي قابل دسترسي است در حالي كه اين كانتينرها را از طريق شبكه خارجي در دسترس ساير كانتينرها قرار مي دهد.
volumes: يك واليوم نامگذاري شده به نام drupal-data را بر روي Mount / var / www / html قرار مي دهيم كه توسط تصوير Drupal ايجاد شده است. استفاده از يك واليوم مشخص از اين طريق به ما امكان مي دهد تا كد برنامه خود را با ساير كانتينرها به اشتراك بگذاريم.
سپس ، تعريف خدمات Nginx را بعد از تعريف سرويس Drupal اضافه خواهيم كرد:
~/drupal/docker-compose.yml

webserver:
image: nginx:1.17.4-alpine
container_name: webserver
depends_on:
– drupal
restart: unless-stopped
ports:
– 80:80
volumes:
– drupal-data:/var/www/html
– ./nginx-conf:/etc/nginx/conf.d
– certbot-etc:/etc/letsencrypt
networks:
– external

مجدداً ، براي شروع كار ، كانتينر خود را نامگذاري مي كنيم و آن را به كانتينر Drupal وابسته مي كنيم. همچنين از يك تصوير alpine– تصوير 1.17.4-alpine Nginx – استفاده مي كنيم .
اين تعريف خدمات نيز گزينه هاي زير را شامل مي شود:
⦁ ports: پورت 80 را براي فعال كردن گزينه هاي پيكربندي تعريف شده در فايل nginx.conf در مرحله 1 در معرض نمايش قرار مي دهد.
⦁ volumes: در اينجا ، ما هم واليوم نامگذاري شده و هم مسير هاست را تعريف مي كنيم:
⦁ drupal-data:/var/www/html : كد برنامه Drupal ما را روي ديركتوري / var / www / html سوار مي كند ، كه ما آن را به عنوان ريشه در بلوك سرور مجازي Nginx قرار داده ايم.
⦁ ./nginx-conf:/etc/nginx/conf.d : دايركتوري پيكربندي Nginx را بر روي هاست براي ديركتوري مربوطه در كانتينر سوار مي كند ، و اطمينان حاصل مي كند كه هرگونه تغييري كه در فايل ها ايجاد كنيم روي هاست در كانتينر منعكس مي شود.
⦁ certbot-etc:/etc/letsencrypt : مجوزها و كليدهاي Let’s Encrypt براي دامنه ما را روي دايركتوري مناسب موجود در كانتينر سوار مي كند.
⦁ networks: ما شبكه خارجي را فقط براي اين تعريف كرده ايم تا اين كانتينر بتواند با كانتينر Drupal و نه با كانتينر mysql ارتباط برقرار كند.
سرانجام آخرين تعريف سرويس خود را براي سرويس certbot اضافه خواهيم كرد. حتماً sammy @ your_domain و your_domain را با ايميل و نام دامنه خود جايگزين كنيد:
~/drupal/docker-compose.yml

certbot:
depends_on:
– webserver
image: certbot/certbot
container_name: certbot
volumes:
– certbot-etc:/etc/letsencrypt
– drupal-data:/var/www/html
command: certonly –webroot –webroot-path=/var/www/html –email sammy@your_domain –agree-tos –no-eff-email –staging -d your_domain -d www.your_domain

اين تعريف به Compose مي گويد تا تصوير certbot / certbot را از Docker Hub دريافت كند. همچنين از اين واليوم هاي نامگذاري شده براي به اشتراك گذاري منابع با كانتينر Nginx ، از جمله گواهينامه هاي دامنه و كليد در certbot-etcو كد برنامه در drupal-data استفاده مي كند.
همچنين از depends_on استفاده كرده ايم تا مطمئن شويم كه پس از اجراي سرويس وب سرور مجازي ، كانتينرهاي certbot شروع مي شوند.
ما هيچ شبكه اي را در اينجا مشخص نكرده ايم زيرا اين كانتينر با هيچ سرويس ديگري از طريق شبكه ارتباط برقرار نخواهد كرد. تنها گواهي نامه هاي دامنه و كليد را اضافه ميكند كه ما با استفاده از واليوم نام گذاري شده نصب كرده ايم.
همچنين گزينه command  را گنجانده ايم كه يك فرمان فرعي را براي اجرا با دستور certbot پيش فرض كانتينر مشخص مي كند. كلاينت Certbot از پلاگين ها براي بدست آوردن و نصب گواهينامه ها پشتيباني مي كند. ما از پلاگين webroot براي بدست آوردن گواهي نامه با درج نام مستقل و – webroot در خط فرمان استفاده مي كنيم. اطلاعات بيشتر در مورد افزونه و دستورات اضافي را از مستندات رسمي Certbot بخوانيد.
پس از تعريف سرويس certbot ، تعريف شبكه و واليوم را اضافه كنيد:
~/drupal/docker-compose.yml

networks:
external:
driver: bridge
internal:
driver: bridge

volumes:
drupal-data:
db-data:
certbot-etc:

كليد شبكه سطح بالا به ما امكان مي دهد شبكه هايي كه بايد ايجاد شوند مشخص كنيم. شبكه ها امكان ارتباط بين سرويس ها و كانتينرهاي موجود در كليه پورت ها را از زمان ورود به سايت فراهم مي كنند زيرا در همان هاست Docker daemon هستند. ما دو شبكه داخلي و خارجي تعريف كرده ايم تا ارتباط وب سرورها ، Drupal و mysql را تضمين كنيم.
از كليد volumes  براي تعريف واليوم هاي نامگذاري drupal-data ، db-data و certbot-etc استفاده مي شود. هنگامي كه Docker واليوم ها را ايجاد مي كند ، محتواي واليوم در يك ديركتوري در سيستم فايل هاست ، / var / lib / docker / volumes / ، كه توسط Docker اداره مي شود ، ذخيره مي گردد. محتويات هر واليوم از اين ديركتوري روي هر كانتينر ديگري كه از واليوم استفاده كند سوار مي شود. به اين ترتيب ، امكان اشتراك گذاري كد و داده ها بين كانتينرها وجود دارد.
فايل نهايي docker-compose.yml اين گونه به نظر مي رسد:
~/drupal/docker-compose.yml
version: “3”

services:
mysql:
image: mysql:8.0
container_name: mysql
command: –default-authentication-plugin=mysql_native_password
restart: unless-stopped
env_file: .env
volumes:
– db-data:/var/lib/mysql
networks:
– internal

drupal:
image: drupal:8.7.8-fpm-alpine
container_name: drupal
depends_on:
– mysql
restart: unless-stopped
networks:
– internal
– external
volumes:
– drupal-data:/var/www/html

webserver:
image: nginx:1.17.4-alpine
container_name: webserver
depends_on:
– drupal
restart: unless-stopped
ports:
– 80:80
volumes:
– drupal-data:/var/www/html
– ./nginx-conf:/etc/nginx/conf.d
– certbot-etc:/etc/letsencrypt
networks:
– external

certbot:
depends_on:
– webserver
image: certbot/certbot
container_name: certbot
volumes:
– certbot-etc:/etc/letsencrypt
– drupal-data:/var/www/html
command: certonly –webroot –webroot-path=/var/www/html –email sammy@your_domain –agree-tos –no-eff-email –staging -d your_domain -d www.your_domain

networks:
external:
driver: bridge
internal:
driver: bridge

volumes:
drupal-data:
db-data:
certbot-etc:

ما كار تعريف سرويس ها را انجام داده ايم. سپس ، بياييد كانتينر را شروع كنيم و درخواست هاي گواهينامه خود را آزمايش كنيم.
مرحله 4 – اخذ گواهينامه ها و اعتبارات SSL
ما مي توانيم كانتينرهاي خود را با دستور docker-compose up شروع كنيم ، كه كانتينرهاي ما را به ترتيبي كه مشخص كرده ايم ، ايجاد و اجرا مي كند. اگر درخواست هاي دامنه ما موفق باشد ، وضعيت خروجي صحيح در خروجي خود و گواهي هاي صحيح نصب شده در پوشه / etc / letsencrypt / live در كانتينر سرور مجازي وب را مشاهده خواهيم كرد.
براي اجراي كانتينرها در پس زمينه ، از دستور docker-compose up با پرچم -d استفاده كنيد:
⦁ $ docker-compose up -d

خروجي مشابهي مشاهده خواهيد كرد كه تأييد مي كند خدمات شما ايجاد شده اند:
Output

Creating mysql … done
Creating drupal … done
Creating webserver … done
Creating certbot … done

با استفاده از دستور docker-compose ps وضعيت خدمات را بررسي كنيد:
⦁ $ docker-compose ps

خدمات mysql ، Drupal و وب سرور را با وضعيتUp مشاهده خواهيم كرد ، در حالي كه certbot با يك پيام وضعيت 0 خارج مي شود:
Output
Name Command State Ports
————————————————————————–
certbot certbot certonly –webroot … Exit 0
drupal docker-php-entrypoint php-fpm Up 9000/tcp
mysql docker-entrypoint.sh –def … Up 3306/tcp, 33060/tcp
webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp

اگر در ستون State براي خدمات mysql ، Drupal يا webserver چيز ديگري به غير از up مشاهده ميكنيد و يا وضعيت خروج غير از 0 براي كانتينر certbot مشاهده مي كنيد ، حتما ورود هاي خدمات را با دستور docker-comps logs بررسي نماييد:
⦁ $ docker-compose logs service_name

اكنون مي توانيم بررسي كنيم كه گواهينامه هاي ما با استفاده از دستور docker-compose exec در كانتينر webserver نصب شده است:
⦁ $ docker-compose exec webserver ls -la /etc/letsencrypt/live

خروجي زير را مي دهد:
Output
total 16
drwx—— 3 root root 4096 Oct 5 09:15 .
drwxr-xr-x 9 root root 4096 Oct 5 09:15 ..
-rw-r–r– 1 root root 740 Oct 5 09:15 README
drwxr-xr-x 2 root root 4096 Oct 5 09:15 your_domain
اكنون كه همه چيز با موفقيت اجرا شد ، مي توانيم تعريف سرويس certbot خود را ويرايش كنيم تا پرچم –staging را حذف كنيم.
فايل docker-compose.yml را باز كنيد ، به تعريف سرويس certbot برويد و پرچم –staging را در گزينه فرمان با پرچم –force-renewal جايگزين كنيد ، كه به Certbot مي گويد كه مي خواهيد يك گواهي جديد را با دامنه هاي مشابه گواهي موجود درخواست دهيد. تعريف certbot به روز شده به شرح زير خواهد بود:
~/drupal/docker-compose.yml

certbot:
depends_on:
– webserver
image: certbot/certbot
container_name: certbot
volumes:
– certbot-etc:/etc/letsencrypt
– drupal-data:/var/www/html
command: certonly –webroot –webroot-path=/var/www/html –email sammy@your_domain –agree-tos –no-eff-email –force-renewal -d your_domain -d www.your_domain

براي ايجاد مجدد كانتينر certbot بايد دوباره docker-compose up را اجرا كنيم. همچنين گزينه –no-deps را در اختيار ميگيريم تا به Compose بگوييم كه مي تواند از شروع سرويس وب سرور مجازي صرفنظر كند ، زيرا در حال حاضر اجرا ميشود:
⦁ $ docker-compose up –force-recreate –no-deps certbot

خروجي را نشان مي دهد كه تاييد ميكند درخواست گواهي ما موفق بوده است:
Output
Recreating certbot … done
Attaching to certbot
certbot | Saving debug log to /var/log/letsencrypt/letsencrypt.log
certbot | Plugins selected: Authenticator webroot, Installer None
certbot | Renewing an existing certificate
certbot | Performing the following challenges:
certbot | http-01 challenge for your_domain
certbot | http-01 challenge for www.your_domain
certbot | Using the webroot path /var/www/html for all unmatched domains.
certbot | Waiting for verification…
certbot | Cleaning up challenges
certbot | IMPORTANT NOTES:
certbot | – Congratulations! Your certificate and chain have been saved at:
certbot | /etc/letsencrypt/live/your_domain/fullchain.pem
certbot | Your key file has been saved at:
certbot | /etc/letsencrypt/live/your_domain/privkey.pem
certbot | Your cert will expire on 2020-01-03. To obtain a new or tweaked
certbot | version of this certificate in the future, simply run certbot
certbot | again. To non-interactively renew *all* of your certificates, run
certbot | “certbot renew”
certbot | – Your account credentials have been saved in your Certbot
certbot | configuration directory at /etc/letsencrypt. You should make a
certbot | secure backup of this folder now. This configuration directory will
certbot | also contain certificates and private keys obtained by Certbot so
certbot | making regular backups of this folder is ideal.
certbot | – If you like Certbot, please consider supporting our work by:
certbot |
certbot | Donating to ISRG / Let’s Encrypt: https://letsencrypt.org/donate
certbot | Donating to EFF: https://eff.org/donate-le
certbot |
certbot exited with code 0

اكنون كه مجوزهاي خود را با موفقيت توليد كرديم ، مي توانيم پيكربندي Nginx خود را به روز كنيم تا SSL را شامل شود.
مرحله 5 – اصلاح تنظيمات وب سرور مجازي و تعريف سرويس
پس از نصب گواهينامه هاي SSL در Nginx ، بايد كليه درخواست هاي HTTP را به HTTPS تغيير مسير دهيم. همچنين بايد مجوز SSL و مكانهاي كليدي خود را مشخص كنيم و پارامترهاي امنيتي و هدرها را اضافه كنيم.
از آنجا كه مي خواهيد سرويس وب سرور مجازي را براي گنجاندن اين موارد اضافه، بازتوليد كنيد ، مي توانيد اكنون آن را متوقف كنيد:
⦁ $ docker-compose stop webserver

خروجي زير را به دست مي دهد:
Output
Stopping webserver … done
سپس ، فايل پيكربندي Nginx را كه قبلاً ايجاد كرديم حذف خواهيم كرد:
⦁ $ rm nginx-conf/nginx.conf

نسخه ديگري از فايل را باز كنيد:
⦁ $ nano nginx-conf/nginx.conf

براي هدايت HTTP به HTTPS و اضافه كردن اعتبار ، پروتكل و هدرهاي امنيتي SSL ، كد زير را به فايل اضافه كنيد. به ياد داشته باشيد كه your_domain را با دامنه خود جايگزين كنيد:
~/drupal/nginx-conf/nginx.conf
server {
listen 80;
listen [::]:80;

server_name your_domain www.your_domain;

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

location / {
rewrite ^ https://$host$request_uri? permanent;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name your_domain www.your_domain;

index index.php index.html index.htm;

root /var/www/html;

server_tokens off;

ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;

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;

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

rewrite ^/core/authorize.php/core/authorize.php(.*)$ /core/authorize.php$1;

location ~ .php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass drupal: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;
}
}

بلوك سرور مجازي HTTP افزونه webroot را براي درخواستهاي تمديد Certbot به دايركتوري .well-known/acme-challenge مشخص مي كند. اين برنامه همچنين شامل يك دستورالعمل rewrite  است كه درخواست هاي HTTP را به ديركتوري اصلي به HTTPS هدايت مي كند.
بلوك سرور مجازي HTTPS ، ssl و http2 را فعال مي كند. براي كسب اطلاعات بيشتر درباره نحوه تكرار HTTP / 2 در پروتكل هاي HTTP و فوايد آن براي عملكرد وب سايت ، لطفاً به مقدمه نحوه تنظيم Nginx با پشتيباني HTTP / 2 در اوبونتو 18.04 مراجعه كنيد.
اين بلوك ها SSL را فعال مي كنند ، همانطور كه گواهي SSL و مكان هاي كليدي ما را همراه با هدرهاي توصيه شده درج كرده ايم. اين هدرها به ما اين امكان را مي دهند كه در سايت هاي آزمايش سرور مجازي SSL Labs و Security Headers امتياز A كسب كنيم.
دستورالعمل هاي root  و index  ما نيز در اين بلوك قرار دارند ، مانند ساير قسمت هاي بلوك مكان ويژه Drupal كه در مرحله 1 بحث شده است.
فايل پيكربندي Nginx به روز شده را ذخيره كنيد و ببنديد.
قبل از استفاده مجدد از كانتينر سرور مجازي ، نياز به اضافه كردن نگاشت پورت 443 روي تعريف سرويس وب سرور مجازي خود خواهيم داشت زيرا مجوزهاي SSL را فعال كرده ايم.
فايل docker-compose.yml را باز كنيد:
⦁ $ nano docker-compose.yml

تغييرات زير را در تعريف سرويس وب سرور مجازي ايجاد كنيد:
~/drupal/docker-compose.yml

webserver:
image: nginx:1.17.4-alpine
container_name: webserver
depends_on:
– drupal
restart: unless-stopped
ports:
– 80:80
– 443:443
volumes:
– drupal-data:/var/www/html
– ./nginx-conf:/etc/nginx/conf.d
– certbot-etc:/etc/letsencrypt
networks:
– external

پس از فعال كردن گواهينامه هاي SSL ، docker-compose.yml به صورت زير ظاهر مي شود:
~/drupal/docker-compose.yml
version: “3”

services:
mysql:
image: mysql:8.0
container_name: mysql
command: –default-authentication-plugin=mysql_native_password
restart: unless-stopped
env_file: .env
volumes:
– db-data:/var/lib/mysql
networks:
– internal

drupal:
image: drupal:8.7.8-fpm-alpine
container_name: drupal
depends_on:
– mysql
restart: unless-stopped
networks:
– internal
– external
volumes:
– drupal-data:/var/www/html

webserver:
image: nginx:1.17.4-alpine
container_name: webserver
depends_on:
– drupal
restart: unless-stopped
ports:
– 80:80
– 443:443
volumes:
– drupal-data:/var/www/html
– ./nginx-conf:/etc/nginx/conf.d
– certbot-etc:/etc/letsencrypt
networks:
– external

certbot:
depends_on:
– webserver
image: certbot/certbot
container_name: certbot
volumes:
– certbot-etc:/etc/letsencrypt
– drupal-data:/var/www/html
command: certonly –webroot –webroot-path=/var/www/html –email sammy@your_domain –agree-tos –no-eff-email –force-renewal -d your_domain -d www.your_domain

networks:
external:
driver: bridge
internal:
driver: bridge

volumes:
drupal-data:
db-data:
certbot-etc:

فايل را ذخيره كنيد و ببنديد. بياييد سرويس وب سرور مجازي را با پيكربندي به روز شده دوباره بازيابي كنيم:
⦁ $ docker-compose up -d –force-recreate –no-deps webserver

خروجي زير را ارائه مي دهد:
Output
Recreating webserver … done
خدمات را با docker-compose ps بررسي كنيد:
⦁ $ docker-compose ps

خدمات mysql ، Drupal و webserver را در حالي مشاهده خواهيم كرد كه certbot با يك پيام وضعيت 0 خارج مي شود:
Output
Name Command State Ports
————————————————————————–
certbot certbot certonly –webroot … Exit 0
drupal docker-php-entrypoint php-fpm Up 9000/tcp
mysql docker-entrypoint.sh –def … Up 3306/tcp, 33060/tcp
webserver nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

در حال حاضر ، تمام خدمات ما در حال اجرا است و بهتر است با نصب Drupal از طريق رابط وب پيش برويم.
مرحله 6 – تكميل نصب از طريق رابط وب
بياييد نصب را از طريق رابط وب Drupal انجام دهيم.
در يك مرورگر وب ، به دامنه سرور برويد. به ياد داشته باشيد كه your_domain خود را در اينجا با نام دامنه خود جايگزين كنيد:
https://your_domain

زبان مورد استفاده را انتخاب كنيد:

روي Save and continue كليك كنيد . به صفحه نمايه نصب منتقل ميشويم. Drupal داراي پروفايل هاي مختلف است ، بنابراين مشخصات Standard را انتخاب كرده و روي Save and continue كليك كنيد.

پس از انتخاب پروفايل ، به صفحه پيكربندي Database خواهيم رفت. نوع Database را به عنوان MySQL ، MariaDB ، Percona Server يا معادل آن انتخاب كنيد و مقادير مربوط به نام پايگاه داده ، نام كاربري و رمز عبور را از بين مقادير مربوط به MYSQL_DATABASE ، MYSQL_USER و MYSQL_PASSWORD به ترتيب در فايل.env در مرحله 2 تعريف كرديد، انتخاب نماييد. روي Advanced Options كليك كرده و مقدار هاست را روي نام كانتينر سرويس mysql تنظيم كنيد. روي Save and continue كليك كنيد.

پس از پيكربندي پايگاه داده ، نصب ماژول ها و تم هاي پيش فرض Drupal شروع مي شود:

پس از نصب سايت ، به صفحه ستاپ سايت پيكربندي Drupal براي پيكربندي نام سايت ، ايميل ، نام كاربري ، رمز عبور و تنظيمات ناحيه اي مي رويم. اطلاعات را پر كنيد و بر روي Save and continue كليك كنيد:

بعد از كليك روي ذخيره و ادامه ، مي توانيم صفحه Welcome to Drupal را مشاهده كنيم ، كه نشان مي دهد سايت Drupal ما با موفقيت شروع به كار كرده است.

اكنون كه نصب Drupal ما تمام شد ، بايد اطمينان حاصل كنيم كه گواهينامه هاي SSL ما به صورت خودكار تمديد خواهد شد.
مرحله 7 – تمديد گواهينامه ها
گواهينامه هاي Let’s Encrypt به مدت 90 روز معتبر هستند ، بنابراين بايد يك فرايند تمديد خودكار را تنظيم كنيم تا اطمينان حاصل شود كه از بين نمي روند. يكي از راه هاي انجام اين كار ايجاد اقدامي با ابزار برنامه ريزي cron است. در اين حالت ، ما يك كار cron ايجاد خواهيم كرد تا به صورت دوره اي يك اسكريپت را اجرا كنيم كه گواهينامه هاي ما را تمديد كرده و پيكربندي Nginx را مجدد لود كند.
بياييد فايل ssl_renew.sh را براي تمديد گواهينامه هاي خود ايجاد كنيم:
⦁ $ nano ssl_renew.sh

كد زير را اضافه كنيد. به ياد داشته باشيد كه نام ديركتوري را با كاربر غير ريشه خود جايگزين كنيد:
~/drupal/ssl_renew.sh
#!/bin/bash

cd /home/sammy/drupal/
/usr/local/bin/docker-compose -f docker-compose.yml run certbot renew –dry-run &&
/usr/local/bin/docker-compose -f docker-compose.yml kill -s SIGHUP webserver

اين اسكريپت در ديركتوري پروژه ~ / drupal تغيير مي كند و دستورات docker-compose زير را اجرا مي كند.
docker-compose run : يك كانتينر certbot را شروع مي كند و فرمان ارائه شده در تعريف سرويس certbot ما را ناديده مي گيرد. به جاي استفاده از فرمان فرعي certonly ، ما در اينجا از فرمان فرعي renew  استفاده مي كنيم ، كه گواهينامه هايي را كه نزديك به انقضا هستند تجديد مي كند. براي آزمايش اسكريپت خود گزينه –dry run را در اينجا گنجانده ايم.
docker-compose kill : يك سيگنال SIGHUP را به كانتينر وب سرور مجازي ارسال مي كند تا پيكربندي Nginx را مجدد لود كند.
با اجراي دستور زير فايل را ببنديد و آن را قابل اجرا كنيد:
⦁ $ sudo chmod +x ssl_renew.sh

در مرحله بعد ، فايل crontab ريشه را باز كنيد تا اسكريپت تجديد در يك بازه مشخص اجرا شود:
⦁ $ sudo crontab -e

اگر اين اولين بار است كه اين فايل را ويرايش مي كنيد ، از شما خواسته مي شود ويرايشگر متن را انتخاب كنيد تا فايل با آن باز شود:
Output
no crontab for root – using an empty one

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

Choose 1-4 [1]:

در انتهاي فايل خط زير را اضافه كنيد و sammy را با نام كاربري خود جايگزين كنيد:
crontab

*/5 * * * * /home/sammy/drupal/ssl_renew.sh >> /var/log/cron.log 2>&1

اين فاصله زماني را براي هر پنج دقيقه تعيين مي كند ، بنابراين مي توانيم آزمايش كنيم كه آيا درخواست تجديد ما مطابق پيش بيني شده كار كرده است يا خير. همچنين يك فايل ورود، cron.log را ايجاد كرده ايم تا خروجي مربوطه را ثبت كنيم.
پس از پنج دقيقه ، از دستور tail استفاده كنيد تا cron.log را بررسي كنيد و ببينيد آيا درخواست تمديد موفقيت آميز بوده است يا خير:
⦁ $ tail -f /var/log/cron.log

خروجي تأييد موفقيت آميز تجديد را مشاهده خواهيد كرد:
Output
** DRY RUN: simulating ‘certbot renew’ close to cert expiry
** (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
/etc/letsencrypt/live/your_domain/fullchain.pem (success)
** DRY RUN: simulating ‘certbot renew’ close to cert expiry
** (The test certificates above have not been saved.)
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –

CTRL + C را فشار دهيد تا از روند tail خارج شود.
اكنون مي توانيم فايل crontab را تغيير دهيم تا اسكريپت روز دوم هر هفته در ساعت 2 صبح اجرا شود. خط آخر crontab را به صورت زير تغيير دهيد:
crontab

* 2 * * 2 /home/sammy/drupal/ssl_renew.sh >> /var/log/cron.log 2>&1

خارج شويد و فايل را ذخيره كنيد.
اكنون ، بياييد گزينه –dry run را از متن ssl_renew.sh حذف كنيم. ابتدا آن را باز كنيد:
⦁ $ nano ssl_renew.sh

سپس محتوا را به شرح زير تغيير دهيد:
~/drupal/ssl_renew.sh
#!/bin/bash

cd /home/sammy/drupal/
/usr/local/bin/docker-compose -f docker-compose.yml run certbot renew &&
/usr/local/bin/docker-compose -f docker-compose.yml kill -s SIGHUP webserver

در حال حاضر كار cron از تمديد گواهينامه هاي SSL با تمديد آنها در صورت واجد شرايط بودن ، مراقبت مي كند.
نتيجه
در اين آموزش از Docker Compose براي ايجاد نصب Drupal با يك وب سرور مجازي Nginx استفاده كرده ايم. به عنوان بخشي از اين گردش كار ، گواهينامه هاي TLS / SSL را براي دامنه مورد نظر خود با سايت Drupal در نظر گرفتيم و يك كار cron ايجاد كرديم تا در صورت لزوم اين گواهينامه ها را تمديد كنيم.
اگر دوست داريد درباره Docker اطلاعات بيشتري كسب كنيد ، به صفحه  Docker topic مراجعه كنيد.