<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mike Brooks</title>
    <description>The latest articles on DEV Community by Mike Brooks (@snpdev).</description>
    <link>https://dev.to/snpdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F153975%2Fbf9e95ca-3042-4570-8a85-04c0fd9bf438.jpeg</url>
      <title>DEV Community: Mike Brooks</title>
      <link>https://dev.to/snpdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/snpdev"/>
    <language>en</language>
    <item>
      <title>CI/CD for WordPress on Azure with GitHub Actions</title>
      <dc:creator>Mike Brooks</dc:creator>
      <pubDate>Thu, 21 Apr 2022 12:33:41 +0000</pubDate>
      <link>https://dev.to/snpdev/cicd-for-wordpress-on-azure-with-github-actions-bn5</link>
      <guid>https://dev.to/snpdev/cicd-for-wordpress-on-azure-with-github-actions-bn5</guid>
      <description>&lt;p&gt;Running WordPress on Azure recently got a whole lot better. In February '22 Microsoft announced &lt;a href="https://techcommunity.microsoft.com/t5/apps-on-azure-blog/the-new-and-better-wordpress-on-app-service/ba-p/3202594"&gt;The new and better ‘WordPress on App Service’&lt;/a&gt; with maintenance and performance enhancements and a streamlined deployment process.&lt;/p&gt;

&lt;p&gt;I'm not going to pontificate about the many benefits of WordPress hosting on Azure suffice to say &lt;strong&gt;SLA&lt;/strong&gt;, &lt;strong&gt;Scale&lt;/strong&gt; and &lt;strong&gt;Security&lt;/strong&gt;. My aim for this post is to address one concern: &lt;strong&gt;how to deploy custom code to your WordPress site running in Azure App Service&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By custom code, I am referring to plugins and themes. If you want to hack WordPress core, you're on your own 😁&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Your code is in a GitHub repository&lt;/li&gt;
&lt;li&gt;You will use the FTPS credentials of your web app. There is a dev.to article for that: &lt;a href="https://dev.to/sfoteini/connect-to-azure-wordpress-app-via-ftp-35m9"&gt;Connect to Azure WordPress App via FTP&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You'll use GitHub Actions for Continuous Integration / Continuous Deployment (CI/CD) to deploy code updates to your WordPress site.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Some Background
&lt;/h2&gt;

&lt;p&gt;WordPress on App Service installs with an Application setting &lt;code&gt;WEBSITES_ENABLE_APP_SERVICE_STORAGE&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt;. This enables persistent storage for your site in an Azure Storage mount having the path &lt;code&gt;/home&lt;/code&gt; OR &lt;code&gt;~/&lt;/code&gt;. Off of this path is the WordPress code (among other stuff like log files and backups).&lt;/p&gt;

&lt;p&gt;As of April 13, 2022, the WordPress source code maintained by Microsoft includes three plugins in the path &lt;code&gt;~/site/wwwroot/wp-content/plugins&lt;/code&gt;. When you install a plugin via the wp-admin UI, this is the path into which they are installed. Themes follow the same pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI/CD For Custom Plugin and Theme Code
&lt;/h2&gt;

&lt;p&gt;Needless to say, for custom code you'll want to have source code version control and an efficient deployment workflow. This is accomplished with GitHub repositories and GitHub Actions.&lt;/p&gt;

&lt;p&gt;For the sake of example, my custom code is a fork of the public repository &lt;strong&gt;&lt;a href="https://github.com/tommcfarlin/wp-hello-world"&gt;tommcfarlin/wp-hello-world&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To our custom code we'll add a directory for our GitHub Action, &lt;code&gt;.github\workflows&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For our GitHub Action we'll use the &lt;strong&gt;&lt;a href="https://github.com/marketplace/actions/ftp-deploy"&gt;FTP Deploy&lt;/a&gt;&lt;/strong&gt; action available in the GitHub Marketplace.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;.github\workflows&lt;/code&gt; directory we'll add a &lt;code&gt;main.yml&lt;/code&gt; file into which we'll insert the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on: push
name: 🚀 Deploy website on push
jobs:
  web-deploy:
    name: 🎉 Deploy
    runs-on: ubuntu-latest
    steps:
    - name: 🚚 Get latest code
      uses: actions/checkout@v2

    - name: 📂 Sync files
      uses: SamKirkland/FTP-Deploy-Action@4.3.0
      with:
        server: &amp;lt;mysite&amp;gt;.ftp.azurewebsites.windows.net
        username: ${{ secrets.WP_USER }}
        password: ${{ secrets.WP_PASSWORD }}
        protocol: ftps        
        server-dir: /site/wwwroot/wp-content/plugins/wp-hello-world/
        # dry-run: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this to work for your site, you need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/Azure/actions-workflow-samples/blob/master/assets/create-secrets-for-GitHub-workflows.md"&gt;Add Actions secrets&lt;/a&gt; for the FTPS credentials. In our example the secret names are &lt;code&gt;WP_USER&lt;/code&gt; and &lt;code&gt;WP_PASSWORD&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For the &lt;code&gt;server:&lt;/code&gt; value replace the &lt;code&gt;&amp;lt;mysite&amp;gt;&lt;/code&gt; placeholder with the unique FTPS endpoint prefix of your site.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Note the &lt;code&gt;# dry-run: true&lt;/code&gt; attribute at the end of the action code. I recommend this for your initial testing of the GitHub Action.&lt;/li&gt;
&lt;li&gt;Refer to the &lt;a href="https://github.com/marketplace/actions/ftp-deploy"&gt;FTP Deploy&lt;/a&gt; documentation for other settings you can employ for your GitHub Action.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Going Deeper
&lt;/h2&gt;

&lt;p&gt;CI/CD really shines when you have staged deployments. With Azure Web Apps you can have &lt;a href="https://docs.microsoft.com/en-us/azure/app-service/deploy-staging-slots"&gt;deployment slots&lt;/a&gt; for pre-production environments such as Dev, Stage, and QA. Each has its own FTPS Endpoint allowing you to leverage GitHub Actions for each stage of your code life-cycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Closing
&lt;/h2&gt;

&lt;p&gt;Thanks for reading this post. I hope you found it helpful. Feel free to share your feedback and suggestions for other readers. &lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/app-service/overview"&gt;App Service overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/app-service/quickstart-wordpress"&gt;Create a WordPress site - Azure App Service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions"&gt;Get Started with GitHub Actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>wordpress</category>
      <category>github</category>
    </item>
    <item>
      <title>Custom App Logging with Azure Monitor for Containers</title>
      <dc:creator>Mike Brooks</dc:creator>
      <pubDate>Tue, 23 Jun 2020 20:47:03 +0000</pubDate>
      <link>https://dev.to/snpdev/custom-app-logging-with-azure-monitor-for-containers-1fm7</link>
      <guid>https://dev.to/snpdev/custom-app-logging-with-azure-monitor-for-containers-1fm7</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Introduction

&lt;ul&gt;
&lt;li&gt;Our Scenario&lt;/li&gt;
&lt;li&gt;Objective&lt;/li&gt;
&lt;li&gt;Solution&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Implementation

&lt;ul&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Step 1 - Invoking syslog in a PHP application&lt;/li&gt;
&lt;li&gt;Step 2 - Dockerfile configurations&lt;/li&gt;
&lt;li&gt;Step 3 - Docker CLI commands&lt;/li&gt;
&lt;li&gt;Step 4 - Deploy to AKS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;In Closing&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;li&gt;Acknowledgements&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you use Azure, you probably spend time in Azure Monitor - Azure's &lt;em&gt;comprehensive solution for collecting, analyzing, and acting on telemetry from your cloud and on-premises environments&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And, if you happen to run containerized workloads in Azure - say with Azure Kubernetes Service (AKS) or Azure Container instances (ACI) - you may already be tapping into &lt;strong&gt;Azure Monitor for containers&lt;/strong&gt;. If you are new to this feature, in short...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Azure Monitor for containers&lt;/strong&gt; gives you performance visibility by collecting memory and processor metrics from controllers, nodes, and containers that are available in Kubernetes through the Metrics API. Container logs are also collected.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For emphasis, I'll now repeat that last sentence...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Container logs are also collected.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Azure Monitor for containers collects stdout and stderr from container workloads deployed to AKS (or ACI). &lt;/p&gt;

&lt;p&gt;Knowing this, all one needs to do is route custom application logs to stderr (or stdout) to take advantage of Azure Monitor for containers.&lt;/p&gt;

&lt;p&gt;In this post, I will walk you through an example implementation using a simple php application and Rsyslog, the &lt;strong&gt;r&lt;/strong&gt;ocket-fast &lt;strong&gt;sys&lt;/strong&gt;tem for &lt;strong&gt;log&lt;/strong&gt; processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our Scenario
&lt;/h3&gt;

&lt;p&gt;We have a containerized PHP application running in Azure Kubernetes Service (AKS). In our application, we leverage the &lt;code&gt;syslog&lt;/code&gt; php function to generate custom log messages, which in turn are consumed by a user defined log handler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Objective
&lt;/h3&gt;

&lt;p&gt;Collect the log messages in Azure Monitor for containers for real-time observation and analysis in a Log Analytics workspace.&lt;/p&gt;

&lt;p&gt;Below are screen captures from Azure Portal that show the expected custom log output in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Live Data (preview) feature (Figure 1) &lt;/li&gt;
&lt;li&gt;A Log Analytics workspace (Figure 2)&lt;/li&gt;
&lt;li&gt;The Logs displayed for our container running in Azure Container Instances (Figure 3)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Figure 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C7am865D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/c0ppj7lht5r9t577mqhc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C7am865D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/c0ppj7lht5r9t577mqhc.png" alt="Screen capture of Live Data logging in AKS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w1HMUsD---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/650l73vobj0j3izkjjbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w1HMUsD---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/650l73vobj0j3izkjjbg.png" alt="Screen capture of custom app log in a Log Analytics workspace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cWAkmcGn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e3e1zjdafz4qoy6cdf5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cWAkmcGn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e3e1zjdafz4qoy6cdf5o.png" alt="Screen capture of container logs in Azure Container Instances"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;In our container image we install the &lt;code&gt;rsyslog&lt;/code&gt; library;  configure a user defined &lt;code&gt;syslog&lt;/code&gt; log handler; and route the messages to &lt;code&gt;stderr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Syslog recognizes the following severity levels for log messages:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Numerical Code&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;emerg&lt;/td&gt;
&lt;td&gt;system is unusable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;alert&lt;/td&gt;
&lt;td&gt;action must be taken immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;crit&lt;/td&gt;
&lt;td&gt;critical conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;error&lt;/td&gt;
&lt;td&gt;error conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;warning&lt;/td&gt;
&lt;td&gt;warning conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;notice&lt;/td&gt;
&lt;td&gt;normal but significant condition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;info&lt;/td&gt;
&lt;td&gt;informational messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;debug&lt;/td&gt;
&lt;td&gt;debug-level messages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In our example implementation, we will evaluate for two severity levels: error (3) and info (6).&lt;/p&gt;

&lt;p&gt;The implementation steps below are taken from this GitHub repo to which I contributed &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/snp-technologies"&gt;
        snp-technologies
      &lt;/a&gt; / &lt;a href="https://github.com/snp-technologies/AKS-Custom-App-Log-Demo"&gt;
        AKS-Custom-App-Log-Demo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;This article assumes experience with Docker containers, Kubernetes and Azure. This article does not describe the steps to install an AKS cluster. Links to a Quickstart tutorial is provided in Resources.&lt;/p&gt;

&lt;p&gt;This article uses a simple PHP application for the implementation. If you're not a PHP programmer, don't despair - the principals covered here are generally applicable to other languages.&lt;/p&gt;

&lt;p&gt;Please note that our image operating system is from the image  &lt;code&gt;debian:stretch-slim&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 - Invoking syslog in a PHP application
&lt;/h3&gt;

&lt;p&gt;The following code example is a simple, single page application I spun up for a "custom application logging with syslog demo". For the demo, &lt;br&gt;
query strings are used to trigger the log handler, such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;?value&lt;span class="o"&gt;=&lt;/span&gt;0 // Triggers an Error log as a result of division by zero.
?value&lt;span class="o"&gt;=&lt;/span&gt;2 // Triggers an Information log.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The application code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;H1&amp;gt;Syslog Demo&amp;lt;/H1&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Fetch the URL query string and assign to a variable. &lt;/span&gt;
&lt;span class="nv"&gt;$qstring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'QUERY_STRING'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nb"&gt;parse_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$qstring&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// For an error evaluation, we divide 1 by $value.&lt;/span&gt;
&lt;span class="c1"&gt;// A value of 0 will throw a division by zero error.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;inverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Division by zero.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Get formatted datetime to include in messages.&lt;/span&gt;
&lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Y/m/d H:i:s"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;p&amp;gt;Inverse of value "&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;" is "&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Inverse of value succeeded"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$severity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;LOG_INFO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$logtext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"INFORMATION: at "&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REMOTE_ADDR'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$logtext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;p&amp;gt;Caught exception: '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$severity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;LOG_ERR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$logtext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: at "&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REMOTE_ADDR'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$logtext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Open and close connection of system logger.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;openlog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"myApp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LOG_PID&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;LOG_PERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LOG_LOCAL0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
  &lt;span class="nb"&gt;syslog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;closelog&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 - Dockerfile configurations
&lt;/h3&gt;

&lt;p&gt;To enable syslog support in our container image, we install the &lt;code&gt;rsyslog&lt;/code&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    rsyslog &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Later in our Dockerfile, we configure a custom log handler for our application logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"local0.* /var/log/apache2/myapp.log"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/rsyslog.conf

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We then configure the log to go to stderr using a symlink, and we re-set ownership of the &lt;code&gt;/var/log/apache2/&lt;/code&gt; just as first set in our base image &lt;code&gt;php:7.3-apache-stretch&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;
  ln -sfT /dev/stderr "/var/log/apache2/myapp.log"; \
  chown -R --no-dereference "www-data:www-data" "/var/log/apache2/"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 - Docker CLI commands
&lt;/h3&gt;

&lt;p&gt;Build a container image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; applogdemo &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Test it locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; applogdemo &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; applogdemo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The log output local test results should look similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt; ok &lt;span class="o"&gt;]&lt;/span&gt; Starting enhanced syslogd: rsyslogd.
AH00558: apache2: Could not reliably determine the server&lt;span class="s1"&gt;'s fully qualified domain name, using 172.17.0.2. Set the '&lt;/span&gt;ServerName&lt;span class="s1"&gt;' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server'&lt;/span&gt;s fully qualified domain name, using 172.17.0.2. Set the &lt;span class="s1"&gt;'ServerName'&lt;/span&gt; directive globally to suppress this message
&lt;span class="o"&gt;[&lt;/span&gt;Sat Jun 20 20:34:48.662731 2020] &lt;span class="o"&gt;[&lt;/span&gt;mpm_prefork:notice] &lt;span class="o"&gt;[&lt;/span&gt;pid 40] AH00163: Apache/2.4.25 &lt;span class="o"&gt;(&lt;/span&gt;Debian&lt;span class="o"&gt;)&lt;/span&gt; PHP/7.3.13 configured &lt;span class="nt"&gt;--&lt;/span&gt; resuming normal operations
&lt;span class="o"&gt;[&lt;/span&gt;Sat Jun 20 20:34:48.662809 2020] &lt;span class="o"&gt;[&lt;/span&gt;core:notice] &lt;span class="o"&gt;[&lt;/span&gt;pid 40] AH00094: Command line: &lt;span class="s1"&gt;'/usr/sbin/apache2 -D FOREGROUND'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;While the container is running, in your browser enter some URL's to invoke the log handler, such as: &lt;br&gt;
&lt;a href="http://localhost/?value=0"&gt;http://localhost/?value=0&lt;/a&gt;, &lt;a href="http://localhost/?value=2"&gt;http://localhost/?value=2&lt;/a&gt;, etc.&lt;/p&gt;

&lt;p&gt;You should see additional log output similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;myApp[41]: ERROR: at 2020/06/29 14:18:15
myApp[41]: Division by zero.
myApp[41]: 172.17.0.1
172.17.0.2:80 172.17.0.1 - - &lt;span class="o"&gt;[&lt;/span&gt;29/Jun/2020:14:18:15 +0000] &lt;span class="s2"&gt;"GET /?value=0 HTTP/1.1"&lt;/span&gt; 200 366 &lt;span class="s2"&gt;"-"&lt;/span&gt; &lt;span class="s2"&gt;"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:7
7.0) Gecko/20100101 Firefox/77.0"&lt;/span&gt;
&lt;span class="s2"&gt;"-"&lt;/span&gt; - - &lt;span class="o"&gt;[&lt;/span&gt;29/Jun/2020:14:18:15 +0000] &lt;span class="s2"&gt;"GET /?value=0 HTTP/1.1"&lt;/span&gt; 200 366 &lt;span class="s2"&gt;"-"&lt;/span&gt; &lt;span class="s2"&gt;"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 F
irefox/77.0"&lt;/span&gt;
myApp[42]: INFORMATION: at 2020/06/29 14:18:30
myApp[42]: Inverse of value succeeded
myApp[42]: 172.17.0.1
172.17.0.2:80 172.17.0.1 - - &lt;span class="o"&gt;[&lt;/span&gt;29/Jun/2020:14:18:30 +0000] &lt;span class="s2"&gt;"GET /?value=4 HTTP/1.1"&lt;/span&gt; 200 268 &lt;span class="s2"&gt;"-"&lt;/span&gt; &lt;span class="s2"&gt;"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:7
7.0) Gecko/20100101 Firefox/77.0"&lt;/span&gt;
&lt;span class="s2"&gt;"-"&lt;/span&gt; - - &lt;span class="o"&gt;[&lt;/span&gt;29/Jun/2020:14:18:30 +0000] &lt;span class="s2"&gt;"GET /?value=4 HTTP/1.1"&lt;/span&gt; 200 268 &lt;span class="s2"&gt;"-"&lt;/span&gt; &lt;span class="s2"&gt;"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now that we have validated the application locally, let's prep the image for publishing to our preferred container registry, Azure Container Registry (Docker Hub or other registry is fine). Let's tag the image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker tag applogdemo myacr.azurecr.io/applogdemo:v1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's push to our preferred container registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker push myacr.azurecr.io/applogdemo:v1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4 - Deploy to AKS
&lt;/h3&gt;

&lt;p&gt;To deploy the container, we use the following Kubernetes YAML manifest.&lt;/p&gt;

&lt;p&gt;Note the inclusion of a Service resource.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are interested in deploying to Azure Container Instances, please refer to the Quickstart tutorial link provided in Resources.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;applogdemo&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;applogdemo&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;applogdemo&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;applogdemo&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myacr.azurecr.io/applogdemo:v1 // Placeholder&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;applogdemo&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;applogdemo-service&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;applogdemo&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Customize the &lt;code&gt;image:&lt;/code&gt; value in the &lt;code&gt;containers:&lt;/code&gt; spec if the &lt;code&gt;deployment.yml&lt;/code&gt; manifest, e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myacr.azurecr.io/applogdemo:v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, deploy the kubernetes manifests using the commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   kubectl apply -f manifests/deployment.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5 - Validate the deployment
&lt;/h3&gt;

&lt;p&gt;Validate the deployment by accessing the website via the IP Address exposed by the Kubernetes LoadBalancer service. To identify the IP Address, use the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get svc drupal-service
NAME             TYPE           CLUSTER-IP   EXTERNAL-IP      PORT(S)        AGE
drupal-service   LoadBalancer   10.2.0.50    52.151.xxx.xxx   80:32758/TCP   10d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Test the application just as done for local testing.&lt;br&gt;
Once you have called a sequence of URL's with the &lt;code&gt;?value=&amp;lt;some integer&amp;gt;&lt;/code&gt; query string go to the Azure Monitor for containers blade in Azure Portal to view your custom application logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Closing
&lt;/h2&gt;

&lt;p&gt;In this article we have covered the basic configurations necessary to enable custom application logging for Azure Monitor for containers.&lt;/p&gt;

&lt;p&gt;From a log analysis standpoint, we've only scratched the  surface in this post. Explore the Kusto query language to craft a wide variety of reports. Create alerts based on log metrics. Pin and share custom log dashboards.&lt;/p&gt;

&lt;p&gt;Please feel free to share your questions and comments in the discussion below. Thanks - Mike 😃&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/azure-monitor/insights/container-insights-overview"&gt;Azure Monitor for containers overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/data-explorer/kusto/concepts/"&gt;Getting started with Kusto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough"&gt;Quickstart: Deploy an Azure Kubernetes Service cluster using the Azure CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/container-instances/container-instances-quickstart"&gt;Quickstart: Deploy a container instance in Azure using the Azure CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rsyslog/rsyslog"&gt;Rsyslog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.php.net/manual/en/function.syslog.php"&gt;PHP syslog function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rsyslog.com/doc/v8-stable/tutorials/log_rotation_fix_size.html"&gt;Log rotation with rsyslog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/docker-library/php/blob/master/7.3/stretch/apache/Dockerfile"&gt;Docker image code for php:7.3-apache-stretch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  👏 Acknowledgements
&lt;/h2&gt;

&lt;p&gt;I would like to thank &lt;a href="https://github.com/firozshk"&gt;Firoz Shaik&lt;/a&gt; for his review of this post and insightful recommendations.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>kubernetes</category>
      <category>aks</category>
      <category>syslog</category>
    </item>
    <item>
      <title>Drupal File Persistence in Azure Kubernetes Service (AKS)</title>
      <dc:creator>Mike Brooks</dc:creator>
      <pubDate>Fri, 05 Jun 2020 12:32:27 +0000</pubDate>
      <link>https://dev.to/snpdev/drupal-file-persistence-in-azure-kubernetes-service-aks-4p85</link>
      <guid>https://dev.to/snpdev/drupal-file-persistence-in-azure-kubernetes-service-aks-4p85</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Drupal file persistence use cases&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;
Configuration steps

&lt;ul&gt;
&lt;li&gt;Step 1 - Dockerfile commands&lt;/li&gt;
&lt;li&gt;Step 2 - PersistentVolume and PersistentVolumeClaim resources&lt;/li&gt;
&lt;li&gt;Step 3 - Add volumes &amp;amp; volumeMounts fields to the K8s Deployment&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;In Closing&lt;/li&gt;

&lt;li&gt;Resources&lt;/li&gt;

&lt;li&gt;Acknowledgements&lt;/li&gt;

&lt;li&gt;Addendum&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As we know, websites are a composite of structured and unstructured data. Text on the webpage may be hard-coded or dynamically aggregated from a database into an HTML file. Image files such as a jpg in-line with page text are independently requested from a file storage location.&lt;/p&gt;

&lt;p&gt;Content management system (CMS) frameworks such as Drupal and WordPress expect to read and write unstructured data files to a directory path, typically on the web server. In the case of Drupal, this file storage location is the &lt;code&gt;/sites/default/files/&lt;/code&gt; directory. For WordPress, it is the &lt;code&gt;wp-content/uploads/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Containerized applications that read and write unstructured data require an external file store resource that persists the files &lt;em&gt;outside&lt;/em&gt; the container. This is because containers, by their nature, are ephemeral. Should the container restart or be updated with a new version, then "zap" ⚡ state written to the running container is gone. The container solution for file persistence is to mount storage to the machine (VM or bare metal) that hosts the container.&lt;/p&gt;

&lt;p&gt;In the case of Kubernetes, our containers run in so-called "pods" which can horizontally scale across a cluster of VM nodes. As such, a "read write many" solution is necessary so that all pods have access to a common file share not pinned to a single node in the cluster.&lt;/p&gt;

&lt;p&gt;In this article we cover a technique to achieve such file persistence in the context of Drupal running in Microsoft's managed service for Kubernetes, Azure Kubernetes Service (or AKS for short).&lt;/p&gt;

&lt;p&gt;While our example uses Drupal, the principals are common to other CMS frameworks based on PHP, such as WordPress and Joomla. With some refactoring, the techniques used in this solution can be applied to other CMS frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drupal file persistence use cases
&lt;/h2&gt;

&lt;p&gt;This tutorial considers three Drupal uses cases for file persistence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Drupal file system&lt;/strong&gt; for a single-site Drupal instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drupal watchdog log&lt;/strong&gt;, as well as logs for PHP errors and Apache access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drupal administration files&lt;/strong&gt; such as a &lt;code&gt;hash_salt&lt;/code&gt; for one-time login links, cancel links, form tokens, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Despite the rudiments covered in the introduction above, this article assumes experience with Drupal, Docker containers, Kubernetes and Azure.&lt;/p&gt;

&lt;p&gt;This article does not describe all of the prerequisites to run Drupal on AKS. For a more comprehensive view, visit this GitHub repository to which I contributed: &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/snp-technologies" rel="noopener noreferrer"&gt;
        snp-technologies
      &lt;/a&gt; / &lt;a href="https://github.com/snp-technologies/Azure-Kubernetes-Service-Drupal8" rel="noopener noreferrer"&gt;
        Azure-Kubernetes-Service-Drupal8
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The method described here uses Azure Files, a managed file storage solution  from Microsoft. A static Azure Storage account and File shares are necessary. You can create these from the Azure Portal by following the steps in &lt;a href="https://docs.microsoft.com/en-us/azure/storage/files/storage-how-to-use-files-portal" rel="noopener noreferrer"&gt;Quickstart: Create and manage Azure file shares with the Azure portal&lt;/a&gt;. As in the Quickstart, for non-production scenarios I recommend the StandardV2 account kind with Locally-redundant storage (LRS) replication. In this tutorial we'll create three files shares corresponding to the aforementioned use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;files&lt;/li&gt;
&lt;li&gt;apache2logs&lt;/li&gt;
&lt;li&gt;configs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Azure Storage Explorer, the resulting Azure Storage account and File shares should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsy8w9vyvcb8mzyt7mmue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsy8w9vyvcb8mzyt7mmue.png" alt="Azure Storage File share screen capture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Alternatively, you can &lt;a href="https://docs.microsoft.com/en-us/azure/aks/azure-files-volume#create-an-azure-file-share" rel="noopener noreferrer"&gt;Create an Azure file share&lt;/a&gt; using the Azure CLI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Configuration steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Dockerfile commands
&lt;/h3&gt;

&lt;p&gt;In the Dockerfile you use to build your container image, include commands to define the directory paths which we will later assign to &lt;code&gt;volumeMounts&lt;/code&gt; in our Kubernetes &lt;code&gt;deployment&lt;/code&gt; manifest. Let's consider the Dockerfile commands for each File share:&lt;/p&gt;

&lt;h4&gt;
  
  
  files
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;RUN mkdir -p /var/www/html/docroot/sites/default/files&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This command assumes that our website DocumentRoot is set to  &lt;code&gt;/var/www/html/docroot&lt;/code&gt;. This can be done in a &lt;code&gt;apache2.conf&lt;/code&gt; file have the line:&lt;br&gt;
&lt;code&gt;DocumentRoot /var/www/html/docroot&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  apache2logs
&lt;/h4&gt;

&lt;p&gt;In our &lt;a href="https://github.com/snp-technologies/Azure-Kubernetes-Service-Drupal8/blob/master/Dockerfile" rel="noopener noreferrer"&gt;example Dockerfile&lt;/a&gt; we use this base image:&lt;br&gt;
&lt;code&gt;FROM php:7.3-apache-stretch&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We are going to use the default location for Apache logs &lt;code&gt;/var/log/apache2/&lt;/code&gt;, and also use this directory for PHP and Drupal logs.&lt;/p&gt;

&lt;p&gt;For the PHP log we include the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'error_log=/var/log/apache2/php-error.log'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'log_errors=On'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'display_errors=Off'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /usr/local/etc/php/php.ini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the Drupal watchdog log we include the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"local0.* /var/log/apache2/drupal.log"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/rsyslog.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This command assumes a local installation of Rsyslog in our container.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  configs
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/www/html/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 - PersistentVolume and PersistentVolumeClaim resources
&lt;/h3&gt;

&lt;p&gt;In this step we compose a Kubernetes manifest with instructions to define &lt;br&gt;
PersistentVolume and PersistentVolumeClaim resources in our AKS cluster. &lt;/p&gt;

&lt;p&gt;Before we do that, however, we need to create a Kubernetes secret to hold the credentials to access our Azure Storage account. The secret manifest YAML is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sa-secrets&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Opaque&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;azurestorageaccountkey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;BASE_64_ENCODED_STORAGE_ACCOUNT_KEY&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;azurestorageaccountname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;BASE_64_ENCODED_STORAGE_ACCOUNT_NAME&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Secret resource will be referenced by our PersistentVolume resource.&lt;/p&gt;

&lt;p&gt;You can find the Azure Storage credentials in the Access keys setting in Azure portal, for example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdxugxw36f8mg91m7oc6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdxugxw36f8mg91m7oc6b.png" alt="Storage account screen capture of Access keys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To output base64 encoded strings for use in your secrets manifest, use the command:&lt;br&gt;
&lt;code&gt;echo -n "&amp;lt;string to encode&amp;gt;" | base64 -w 0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;OK. Now that we've gotten that out of the way, we can turn our attention back to our PersistentVolume and PersistentVolumeClaim resources. The YAML example below illustrates the creation of a PersistentVolume and a PersistentVolumeClaim to be used for our public file system path. &lt;/p&gt;

&lt;p&gt;Please note...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our use of the &lt;code&gt;azurefile&lt;/code&gt; storageClassName in the PersistentVolume spec. This StorageClass as well as &lt;code&gt;azurefile-premium&lt;/code&gt; are Kubernetes resources provided out-of-the-box in AKS.&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;mountOptions&lt;/code&gt; we set the file share access control. &lt;code&gt;uid=1000&lt;/code&gt; refers to user &lt;code&gt;d8admin&lt;/code&gt; added in our Dockerfile and &lt;code&gt;uid=33&lt;/code&gt; refers to the group which happens to be &lt;code&gt;www-data&lt;/code&gt; provided by the container's base image (see the Addendum for a how-to).
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolume&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;filespv&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteMany&lt;/span&gt;
  &lt;span class="na"&gt;storageClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;azurefile&lt;/span&gt;
  &lt;span class="na"&gt;capacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5Gi&lt;/span&gt;
  &lt;span class="na"&gt;azureFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sa-secrets&lt;/span&gt;
    &lt;span class="na"&gt;shareName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;files&lt;/span&gt;
    &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;claimRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;filespvc&lt;/span&gt;
  &lt;span class="na"&gt;mountOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dir_mode=0777&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;file_mode=0777&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;uid=1000&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gid=33&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mfsymlinks&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nobrl&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;filespvc&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteMany&lt;/span&gt;
  &lt;span class="na"&gt;storageClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;azurefile&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5Gi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Similarly, PersistentVolume and PersistentVolumeClaim resources can be created for the &lt;code&gt;apache2logs&lt;/code&gt; and &lt;code&gt;configs&lt;/code&gt; File shares. &lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3 - Add volumes &amp;amp; volumeMounts fields to the K8s Deployment
&lt;/h3&gt;

&lt;p&gt;The final step to ensure file persistance is to make our Drupal pod(s) aware of the Azure Files' persistent volume claims, and associate each claim with a path in our container.&lt;/p&gt;

&lt;p&gt;In our Kubernetes deployment YAML manifest we add &lt;code&gt;.spec.containers.volumeMounts&lt;/code&gt; and &lt;code&gt;.spec.volumes&lt;/code&gt; fields.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;registry&amp;gt;/&amp;lt;image-name&amp;gt;:&amp;lt;tag&amp;gt;&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;drupal&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/log/apache2&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache2-vol&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/www/html/docroot/sites/default/files&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;files-vol&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/www/html/config&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config-vol&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config-vol&lt;/span&gt;
          &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;claimName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;configpvc&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;files-vol&lt;/span&gt;
          &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;claimName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;filespvc&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache2-vol&lt;/span&gt;
          &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;claimName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache2pvc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A complete deployment example can be found in the aforementioned &lt;a href="https://github.com/snp-technologies/Azure-Kubernetes-Service-Drupal8" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Closing
&lt;/h2&gt;

&lt;p&gt;In this article we have covered the basic configurations necessary to enable file persistent for a Drupal deployment in Azure Kubernetes Service.&lt;/p&gt;

&lt;p&gt;Drupal containerization and Kubernetes orchestration are a craft and there are many opinions on how to do it. The technique described here is one of many options for file persistence. I encourage you to explore the storage options to determine what is best your use case, whether it is Azure Files, an NFS server, GlusterFS, or even a storage abstraction layer such as Rook or Portworx.&lt;/p&gt;

&lt;p&gt;Please feel free to share your questions and comments in the discussion below. Thanks - Mike 😃&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/aks/azure-files-volume" rel="noopener noreferrer"&gt;Manually create and use a volume with Azure Files share in Azure Kubernetes Service (AKS)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/storage/files/storage-how-to-use-files-portal" rel="noopener noreferrer"&gt;Quickstart: Create and manage Azure file shares with the Azure portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/node/15368" rel="noopener noreferrer"&gt;Drupal File system settings documentation on drupal.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/snp-technologies/Azure-Kubernetes-Service-Drupal8" rel="noopener noreferrer"&gt;A Drupal on AKS solution repo on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rsyslog.com/doc/v8-stable/tutorials/log_rotation_fix_size.html" rel="noopener noreferrer"&gt;Log rotation with rsyslog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  👏 Acknowledgements
&lt;/h2&gt;

&lt;p&gt;I would like to thank &lt;a href="https://github.com/gousiya573-snp" rel="noopener noreferrer"&gt;Gousiya Sayyad&lt;/a&gt; for her technical know-how and review of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addendum
&lt;/h2&gt;

&lt;p&gt;To get the uid and gid values for my &lt;code&gt;mountOptions&lt;/code&gt; properties, I bash into my container running locally, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;SNP+mike@MIKE-T570 MINGW64 ~
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;winpty docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; drupal8aks bash
&lt;span class="gp"&gt;root@6cc24a823fb3:/var/www/html#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; d8admin
&lt;span class="go"&gt;1000
&lt;/span&gt;&lt;span class="gp"&gt;root@6cc24a823fb3:/var/www/html#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; www-data
&lt;span class="go"&gt;33
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>drupal</category>
      <category>azure</category>
      <category>aks</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
