Having the ability to containerize your Windows based application and running them on Kubernetes is a great options, specially for companies who are not looking forward to modernize their application using a cross platforms frameworks.

While containerizing Windows based application is pretty much easy, being able to control the time zone of the windows based nodes is kind of tricky specially when using Azure Kubernetes Services (AKS), or Amazon EKS. hosted on a different region.

lets go through one of the options we have to overcome this issue.

The idea is to create a K8s DaemonSet which is going to ensure a specific pod to be deployed to each and every node that exists or being added to the cluster, that pod will simply connect to its host node and set the timezone to your proffered one.

Preparing a docker image for you the DaemonSet Pod

First you need to have Docker Desktop downloaded and installed on your development machine.

https://www.docker.com/products/docker-desktop

Make sure you switch to windows based engine. now create an empty folder in which we are going to use to build our image, lets name it TimeZoneImage.

we are going to need a tool which will help us ssh inside the node from the pod, one advantage plink is the ability to automate passing in the username and password of an SSH session, download plink.exe 64bits from the link below and store it inside TimeZoneImage folder.

https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html

we need to pass in a script which will be executed remotely on the server so create a new file bat file lets call it script.bat

script.bat

powershell Set-TimeZone -Name '#TimeZone#'; echo "Done execting inside the Node!...";echo "Changing Time Zone to: #TimeZone#";

This will simply set the time zone to your TimeZone Env Variable.

Next we need a file to act as an entrypoint for our container, lets create that file and call it start.ps1

start.ps1

echo "Start Plink with Node $env:nodeIP"
$filePath = 'script.bat'
$tempFilePath = "$env:TEMP\$($filePath | Split-Path -Leaf)"
$find = '#TimeZone#'
$replace = $env:timezone

(Get-Content -Path $filePath) -replace $find, $replace | Add-Content -Path $tempFilePath

Remove-Item -Path $filePath
Move-Item -Path $tempFilePath -Destination $filePath

do{

echo $("Setting TimeZone to Arab Time Zone at " + $(Get-Date).ToString())
echo y | c:\\plink.exe $env:nodeIP -l $env:user -pw $env:pwd -m script.bat
echo "Done Setting TimeZone for this round!..."
sleep 1440
}while($true)

Echo "Done Plink"

Next the DockerFile would look like this

FROM mcr.microsoft.com/windows/servercore:ltsc2019
COPY plink.exe /
COPY script.bat /
COPY start.ps1 /
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; Set-ExecutionPolicy Unrestricted -Force;"]
ENTRYPOINT ["powershell.exe", "c:\\start.ps1"] 

The folder should look like this

Open power-shell and from inside the folder run the following command to build the image, tag it and push it to your favourite container register, (In this case I will use Azure Container Registry)

docker build .
docker tag <yourimage> <youracr>.azurecr.io/<yourimagename>
docker push <yourimage> <youracr>.azurecr.io/<yourimagename>

once done we are ready to create our DaemonSet

DaemonSet ensures that all (or some) Nodes run a copy of a Pod. As nodes are added to the cluster, Pods are added to them. As nodes are removed from the cluster, those Pods are garbage collected. Deleting a DaemonSet will clean up the Pods it created.

https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/

User the following Yaml file to deploy DaemonSet:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  namespace: kube-system
  name: wintimezonedaemon-ds
spec:
  selector:
    matchLabels:
      name: wintimezonedaemon-ds
  template:
    metadata:
      labels:
        name: wintimezonedaemon-ds
    spec:
      nodeSelector:
        "beta.kubernetes.io/os": windows
      containers:
      - name: wintimezonedaemon
        env:
          - name: nodeIP
            valueFrom:
              fieldRef:
                fieldPath: status.hostIP
          - name: timezone
            value: <yourpreferedtimezone>
          - name: user
            value: <your-vmss-or-node-user>
          - name: pwd
            value: <your-vmss-or-node-password>
        image: <youracr>.azurecr.io/<yourimagename>:latest
        resources:
          limits:
            cpu: 300m
            memory: 400M
          requests:
            cpu: 150m
            memory: 200M

Note the environment variables, the first one is to pass in the NodeIP to the pod which is going to be used to communicate with the node, the second env variable is where you provide your preferred time zone the third and fourth env variables are basically your windows node username and password if you need to reset this you can go to the VMSS on azure portal (Assuming AK) and choose “Reset Password”, .

once done you can simply execute the following command to create your DaemonSet

kubectl create -f daemonset.yaml

Check Github for the source code.

https://github.com/mohaom/TimeZoneImage

for a ready made image use the following:

https://hub.docker.com/r/omarse/windowstimezoner

or directly pull using

docker pull omarse/windowstimezoner

 docker pull omarse/windowstimezoner:latest

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s