Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Hyper-v containers not working on kubernetes v1.10 #2627

Closed
brusMX opened this issue Apr 7, 2018 · 20 comments · Fixed by #3753
Closed

Hyper-v containers not working on kubernetes v1.10 #2627

brusMX opened this issue Apr 7, 2018 · 20 comments · Fixed by #3753
Assignees
Labels

Comments

@brusMX
Copy link

brusMX commented Apr 7, 2018

Is this a request for help?:

Yes


Is this an ISSUE or FEATURE REQUEST? (choose one): Feature request


What version of acs-engine?:

Version: canary
GitCommit: b143b6d
GitTreeState: clean


Orchestrator and version (e.g. Kubernetes, DC/OS, Swarm)
Kubernetes

What happened:
Deployed a hybrid kubernetes cluster (Win/Linux) Hyper-v enabled, but it won't deploy hyper-v isolated containers.

This is my hyperv-cluster.json file:

{
    "apiVersion": "vlabs",
    "properties": {
        "orchestratorProfile": {
            "orchestratorType": "Kubernetes",
            "orchestratorRelease": "1.10",
            "kubernetesConfig": {
               "apiServerConfig" : {
                 "--feature-gates": "HyperVContainer=true"
               }
           }
        },
        "masterProfile": {
            "count": 1,
            "dnsPrefix": "hyperv-cluster-012",
            "vmSize": "Standard_DS2_v2"
        },
        "agentPoolProfiles": [
            {
                "name": "linuxpool1",
                "count": 1,
                "vmSize": "Standard_DS2_v2",
                "storageProfile" : "ManagedDisks",
                "availabilityProfile": "AvailabilitySet"
            },
            {
                "name": "windowspool1",
                "count": 1,
                "vmSize": "Standard_D2_v3",
                "availabilityProfile": "AvailabilitySet",
                "osType": "Windows"
            }
        ],
        "windowsProfile": {
            "adminUsername": "azureuser1",
            "adminPassword": "pss10rdw!"
        },
        "linuxProfile": {
            "adminUsername": "azureuser1",
            "ssh": {
                "publicKeys": [
                    {
                        "keyData": ""
                    }
                ]
            }
        },
        "servicePrincipalProfile": {
            "clientId": "",
            "secret": ""
        }
    }
}

And I am deploying a yaml file like this, hyperv-win-sever.yaml:

apiVersion: v1
kind: Service
metadata:
  name: win-webserver
  labels:
    app: win-webserver
spec:
  ports:
    # the port that this service should serve on
  - port: 80
    targetPort: 80
  selector:
    app: win-webserver
  type: LoadBalancer
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: win-webserver
  name: win-webserver
spec:
  replicas: 1
  template:
    metadata:
      annotations:
        experimental.windows.kubernetes.io/isolation-type: hyperv
      labels:
        app: win-webserver
      name: win-webserver
    spec:
      containers:
      - name: windowswebserver
        image: microsoft/windowsservercore:1709
        command:
        - powershell.exe
        - -command
        - "<#code used from https://gist.github.com/wagnerandrade/5424431#> ; $$listener = New-Object System.Net.HttpListener ; $$listener.Prefixes.Add('http://*:80/') ; $$listener.Start() ; $$callerCounts = @{} ; Write-Host('Listening at http://*:80/') ; while ($$listener.IsListening) { ;$$context = $$listener.GetContext() ;$$requestUrl = $$context.Request.Url ;$$clientIP = $$context.Request.RemoteEndPoint.Address ;$$response = $$context.Response ;Write-Host '' ;Write-Host('> {0}' -f $$requestUrl) ;  ;$$count = 1 ;$$k=$$callerCounts.Get_Item($$clientIP) ;if ($$k -ne $$null) { $$count += $$k } ;$$callerCounts.Set_Item($$clientIP, $$count) ;$$header='<html><body><H1>Windows Container Web Server</H1>' ;$$callerCountsString='' ;$$callerCounts.Keys | % { $$callerCountsString+='<p>IP {0} callerCount {1} ' -f $$_,$$callerCounts.Item($$_) } ;$$footer='</body></html>' ;$$content='{0}{1}{2}' -f $$header,$$callerCountsString,$$footer ;Write-Output $$content ;$$buffer = [System.Text.Encoding]::UTF8.GetBytes($$content) ;$$response.ContentLength64 = $$buffer.Length ;$$response.OutputStream.Write($$buffer, 0, $$buffer.Length) ;$$response.Close() ;$$responseStatus = $$response.StatusCode ;Write-Host('< {0}' -f $$responseStatus)  } ; "
      nodeSelector:
        beta.kubernetes.io/os: windows

What you expected to happen:

When I RDP into the windows container and RDP into the machine I don't get a hyper-v Isolated container, docker inspect hyperv-winserver. All I get is "Isolation": "process"

[
    {
        "Id": "a7641e7f529a60725c2849df5b110124bfc1831e2b392e1b10c410f280f5167d",
        "Created": "2018-04-07T00:03:44.0243269Z",
        "Path": "powershell.exe",
        "Args": [
            "-command",
            "<#code used from https://gist.github.com/wagnerandrade/5424431#> ; $listener = New-Object System.Net.HttpListener ; $listener.Prefixes.Add('http://*:80/') ; $listener.Start() ; $callerCounts = @{} ; Write-Host('Listening at http://*:80/') ; while ($listener.IsListening) { ;$context = $listener.GetContext() ;$requestUrl = $context.Request.Url ;$clientIP = $context.Request.RemoteEndPoint.Address ;$response = $context.Response ;Write-Host '' ;Write-Host('> {0}' -f $requestUrl) ;  ;$count = 1 ;$k=$callerCounts.Get_Item($clientIP) ;if ($k -ne $null) { $count += $k } ;$callerCounts.Set_Item($clientIP, $count) ;$header='<html><body><H1>Windows Container Web Server</H1>' ;$callerCountsString='' ;$callerCounts.Keys | % { $callerCountsString+='<p>IP {0} callerCount {1} ' -f $_,$callerCounts.Item($_) } ;$footer='</body></html>' ;$content='{0}{1}{2}' -f $header,$callerCountsString,$footer ;Write-Output $content ;$buffer = [System.Text.Encoding]::UTF8.GetBytes($content) ;$response.ContentLength64 = $buffer.Length ;$response.OutputStream.Write($buffer, 0, $buffer.Length) ;$response.Close() ;$responseStatus = $response.StatusCode ;Write-Host('< {0}' -f $responseStatus)  } ; "
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 1664,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2018-04-07T00:03:47.1174979Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:6e8857bf419a694d7f4177f57ea9770a574f1ecb04ff204bd73b1bc540ee8876",
        "ResolvConfPath": "",
        "HostnamePath": "",
        "HostsPath": "",
        "LogPath": "C:\\ProgramData\\docker\\containers\\a7641e7f529a60725c2849df5b110124bfc1831e2b392e1b10c410f280f5167d\\a7641e7f529a60725c2849df5b110124bfc1831e2b392e1b10c410f280f5167d-json.log",
        "Name": "/k8s_windowswebserver_win-webserver-df9cb574d-6qwzv_default_25cbb079-39f3-11e8-b6b6-000d3a717dcd_1",
        "RestartCount": 0,
        "Driver": "windowsfilter",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": [
                "c:\\var\\lib\\kubelet\\pods\\25cbb079-39f3-11e8-b6b6-000d3a717dcd\\volumes\\kubernetes.io~secret\\default-token-pq297:c:/var/run/secrets/kubernetes.io/serviceaccount:ro"
            ],
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "container:7c884f470ed4145c72b80e633b056109f470a4aaf73d453e024a983e99b43537",
            "PortBindings": null,
            "RestartPolicy": {
                "Name": "",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Dns": null,
            "DnsOptions": null,
            "DnsSearch": null,
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "container:7c884f470ed4145c72b80e633b056109f470a4aaf73d453e024a983e99b43537",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "process",
            "CpuShares": 5000,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": null,
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DiskQuota": 0,
            "KernelMemory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": null,
            "PidsLimit": 0,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0
        },
        "GraphDriver": {
            "Data": {
                "dir": "C:\\ProgramData\\docker\\windowsfilter\\a7641e7f529a60725c2849df5b110124bfc1831e2b392e1b10c410f280f5167d"
            },
            "Name": "windowsfilter"
        },
        "Mounts": [
            {
                "Type": "bind",
                "Source": "c:\\var\\lib\\kubelet\\pods\\25cbb079-39f3-11e8-b6b6-000d3a717dcd\\volumes\\kubernetes.io~secret\\default-token-pq297",
                "Destination": "c:\\var\\run\\secrets\\kubernetes.io\\serviceaccount",
                "Mode": "",
                "RW": false,
                "Propagation": ""
            }
        ],
        "Config": {
            "Hostname": "win-webserver-df9cb574d-6qwzv",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "WIN_WEBSERVER_PORT_80_TCP_PORT=80",
                "WIN_WEBSERVER_PORT_80_TCP_ADDR=10.0.100.229",
                "KUBERNETES_PORT_443_TCP_PORT=443",
                "WIN_WEBSERVER_PORT_80_TCP_PROTO=tcp",
                "KUBERNETES_SERVICE_PORT=443",
                "KUBERNETES_PORT_443_TCP_PROTO=tcp",
                "WIN_WEBSERVER_SERVICE_PORT=80",
                "KUBERNETES_SERVICE_HOST=10.0.0.1",
                "KUBERNETES_SERVICE_PORT_HTTPS=443",
                "KUBERNETES_PORT=tcp://10.0.0.1:443",
                "KUBERNETES_PORT_443_TCP=tcp://10.0.0.1:443",
                "KUBERNETES_PORT_443_TCP_ADDR=10.0.0.1",
                "WIN_WEBSERVER_SERVICE_HOST=10.0.100.229",
                "WIN_WEBSERVER_PORT=tcp://10.0.100.229:80",
                "WIN_WEBSERVER_PORT_80_TCP=tcp://10.0.100.229:80"
            ],
            "Cmd": null,
            "Healthcheck": {
                "Test": [
                    "NONE"
                ]
            },
            "Image": "microsoft/windowsservercore@sha256:dfc84737964de95ec888ae25aa70affd815ded546e30491dec630205f8297012",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": [
                "powershell.exe",
                "-command",
                "<#code used from https://gist.github.com/wagnerandrade/5424431#> ; $listener = New-Object System.Net.HttpListener ; $listener.Prefixes.Add('http://*:80/') ; $listener.Start() ; $callerCounts = @{} ; Write-Host('Listening at http://*:80/') ; while ($listener.IsListening) { ;$context = $listener.GetContext() ;$requestUrl = $context.Request.Url ;$clientIP = $context.Request.RemoteEndPoint.Address ;$response = $context.Response ;Write-Host '' ;Write-Host('> {0}' -f $requestUrl) ;  ;$count = 1 ;$k=$callerCounts.Get_Item($clientIP) ;if ($k -ne $null) { $count += $k } ;$callerCounts.Set_Item($clientIP, $count) ;$header='<html><body><H1>Windows Container Web Server</H1>' ;$callerCountsString='' ;$callerCounts.Keys | % { $callerCountsString+='<p>IP {0} callerCount {1} ' -f $_,$callerCounts.Item($_) } ;$footer='</body></html>' ;$content='{0}{1}{2}' -f $header,$callerCountsString,$footer ;Write-Output $content ;$buffer = [System.Text.Encoding]::UTF8.GetBytes($content) ;$response.ContentLength64 = $buffer.Length ;$response.OutputStream.Write($buffer, 0, $buffer.Length) ;$response.Close() ;$responseStatus = $response.StatusCode ;Write-Host('< {0}' -f $responseStatus)  } ; "
            ],
            "OnBuild": null,
            "Labels": {
                "annotation.io.kubernetes.container.hash": "7a74b290",
                "annotation.io.kubernetes.container.restartCount": "1",
                "annotation.io.kubernetes.container.terminationMessagePath": "/dev/termination-log",
                "annotation.io.kubernetes.container.terminationMessagePolicy": "File",
                "annotation.io.kubernetes.pod.terminationGracePeriod": "30",
                "io.kubernetes.container.logpath": "\\var\\log\\pods\\25cbb079-39f3-11e8-b6b6-000d3a717dcd\\windowswebserver\\1.log",
                "io.kubernetes.container.name": "windowswebserver",
                "io.kubernetes.docker.type": "container",
                "io.kubernetes.pod.name": "win-webserver-df9cb574d-6qwzv",
                "io.kubernetes.pod.namespace": "default",
                "io.kubernetes.pod.uid": "25cbb079-39f3-11e8-b6b6-000d3a717dcd",
                "io.kubernetes.sandbox.id": "7c884f470ed4145c72b80e633b056109f470a4aaf73d453e024a983e99b43537"
            }
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "",
            "Gateway": "",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "IPv6Gateway": "",
            "MacAddress": "",
            "Networks": {}
        }
    }
]

Nevertheless, when I run:

docker run -it --isolation=hyperv microsoft/nanoserver cmd

I totally get a hyperv isolated container.

How to reproduce it (as minimally and precisely as possible):

As described above, deploy a cluster and deploy the yaml deployment, rdp into the vm and docker inspect the container.

Anything else we need to know:

I know it's super experimental, but we are running an investigation on Windows Containers and I would definitely appreciate your feedback on how to get this working. Thanks!

@feiskyer
Copy link
Member

feiskyer commented Apr 7, 2018

@brusMX Seems feature gates HyperVContainer=true is not enabled on kubelet. Please check whether it is included in kubelet params.

@brusMX
Copy link
Author

brusMX commented Apr 9, 2018

@feiskyer that is a tricky question. By definition in the json file is enabled.
Nevertheless, when I SSH into the master I see that:

  1. My kubelet process does not have hyperv as an argument in features-gates.
  2. /hyperkube apiserver does have the flag --feature-gates=HyperVContainer=true

What does this mean?

Here is the command I ran to verify this:

azureuser1@k8s-master-86740673-0:~$ ps aux | grep feature-gates

root       9543  2.2  1.7 682956 126280 ?       Ssl  Apr06  90:54 /usr/local/bin/kubelet 
--enable-server 
--node-labels=kubernetes.io/role=master,kubernetes.azure.com/cluster=hybridk8srg012 
--v=2 --volume-plugin-dir=/etc/kubernetes/volumeplugins --address=0.0.0.0 
--allow-privileged=true --anonymous-auth=false --authorization-mode=Webhook
 --azure-container-registry-config=/etc/kubernetes/azure.json --cadvisor-port=0 
--cgroups-per-qos=true --client-ca-file=/etc/kubernetes/certs/ca.crt 
--cloud-config=/etc/kubernetes/azure.json --cloud-provider=azure 
--cluster-dns=10.0.0.10 --cluster-domain=cluster.local --enforce-node-allocatable=pods 
--event-qps=0 
--eviction-hard=memory.available<100Mi,nodefs.available<10%,nodefs.inodesFree<5% 
**--feature-gates=** --image-gc-high-threshold=85 --image-gc-low-threshold=80 
--keep-terminated-pod-volumes=false --kubeconfig=/var/lib/kubelet/kubeconfig 
--max-pods=30 --network-plugin=cni --node-status-update-frequency=10s 
--non-masquerade-cidr=10.240.0.0/12 
--pod-infra-container-image=k8s-gcrio.azureedge.net/pause-amd64:3.1 
--pod-manifest-path=/etc/kubernetes/manifests 
--register-node=true 
--register-with-taints=node-role.kubernetes.io/master=true:NoSchedule


root       9970  4.7  8.6 968448 617152 ?       Ssl  Apr06 189:46 /hyperkube apiserver 
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota,DenyEscalatingExec,AlwaysPullImages 
--advertise-address=10.255.255.5 --allow-privileged=true --anonymous-auth=false 
--audit-log-maxage=30 --audit-log-maxbackup=10 --audit-log-maxsize=100 
--audit-log-path=/var/log/audit.log --authorization-mode=Node,RBAC 
--bind-address=0.0.0.0 --client-ca-file=/etc/kubernetes/certs/ca.crt 
--etcd-cafile=/etc/kubernetes/certs/ca.crt --etcd-certfile=/etc/kubernetes/certs/etcdclient.crt 
--etcd-keyfile=/etc/kubernetes/certs/etcdclient.key --etcd-quorum-read=true 
--etcd-servers=https://127.0.0.1:2379 
**--feature-gates=HyperVContainer=true** 
--insecure-port=8080 --kubelet-client-certificate=/etc/kubernetes/certs/client.crt 
--kubelet-client-key=/etc/kubernetes/certs/client.key --profiling=false 
--repair-malformed-updates=false --secure-port=443 
--service-account-key-file=/etc/kubernetes/certs/apiserver.key 
--service-account-lookup=true 
--service-cluster-ip-range=10.0.0.0/16 --storage-backend=etcd3 
--tls-cert-file=/etc/kubernetes/certs/apiserver.crt 
--tls-private-key-file=/etc/kubernetes/certs/apiserver.key --v=4

root      10023  2.9  2.1 416824 149500 ?       Ssl  Apr06 117:27 /hyperkube controller-manager --allocate-node-cidrs=false --cloud-config=/etc/kubernetes/azure.json --cloud-provider=azure --cluster-cidr=10.240.0.0/12 --cluster-name=hybrid-cluster-012 --cluster-signing-cert-file=/etc/kubernetes/certs/ca.crt --cluster-signing-key-file=/etc/kubernetes/certs/ca.key --feature-gates=ServiceNodeExclusion=true --kubeconfig=/var/lib/kubelet/kubeconfig --leader-elect=true --node-monitor-grace-period=40s --pod-eviction-timeout=5m0s --profiling=false --root-ca-file=/etc/kubernetes/certs/ca.crt --route-reconciliation-period=10s --service-account-private-key-file=/etc/kubernetes/certs/apiserver.key --terminated-pod-gc-threshold=5000 --use-service-account-credentials=true --v=2

root      10560  0.1  1.2 352544 88300 ?        Ssl  Apr06   5:23 /hyperkube proxy --kubeconfig=/var/lib/kubelet/kubeconfig --cluster-cidr=10.240.0.0/12 --feature-gates=ExperimentalCriticalPodAnnotation=true

azureus+  33464  0.0  0.0  12916   936 pts/0    S+   18:26   0:00 grep --color=auto feature-gates

@brusMX
Copy link
Author

brusMX commented Apr 9, 2018

On the windows node, when I see at the kubelet process I see that it doesn't have the --feature-gates tag:

PS C:\k> $process = "kubelet.exe"
>> Get-WmiObject Win32_Process 
-Filter "name = '$process'" | Select-Object CommandLine | out-string -Width 2000

CommandLine 
----------- 
"C:\k\kubelet.exe" --hostname-override=86740k8s9010 
--pod-infra-container-image=kubletwin/pause --resolv-conf= --allow-privileged=true 
--enable-debugging-handlers --cluster-dns=10.0.0.10 
--cluster-domain=cluster.local --kubeconfig=c:\k\config 
--hairpin-mode=promiscuous-bridge --v=2 
--azure-container-registry-config=c:\k\azure.json --runtime-request-timeout=10m 
--cloud-provider=azure --cloud-config=c:\k\azure.json 
--image-pull-progress-deadline=20m --cgroups-per-qos=false -
-enforce-node-allocatable= --volume-plugin-dir=c:\k\volumeplugins 
--network-plugin=cni --cni-bin-dir=c:\k\azurecni\bin 
--cni-conf-dir=c:\k\azurecni\netconf

@brusMX brusMX changed the title Hyper-v containers on kubernetes v1.10 Hyper-v containers not working on kubernetes v1.10 Apr 9, 2018
@brusMX
Copy link
Author

brusMX commented Apr 9, 2018

@JiangtianLi Any pointers on this?

@brusMX
Copy link
Author

brusMX commented Apr 9, 2018

Ok, so I got it working with a few hacks.
This is the bug to be corrected to make HyperV containers work out of the box with ACS Engine:

[BUG] Missing "HyperV" flag on Kubelet.

Kubelet is not being started with the flag --feature-gates=HyperVContainer=true despite having the correct configuration on the cluster json file definition.

"orchestratorProfile": {
            "orchestratorType": "Kubernetes",
            "orchestratorRelease": "1.10",
            "kubernetesConfig": {
               "apiServerConfig" : {
                 "--feature-gates": "HyperVContainer=true"
                }
            }
}

To hack this manually you need to add this flag to kubelet in all the masters and all the nodes (at least the windows nodes).

Add HyperV flag on Linux Masters:

  1. SSH into the master.
  2. Edit sudo vi /etc/default/kubelet file and add --feature-gates=HyperVContainer=true to the KUBELET_CONFIG set of parameters.
  3. Restart kubelet with sudo systemctl restart kubelet.
  4. Make sure it restarted succesfully with sudo systemctl status kubelet.
  5. Verify that kubelet was run with HypeVContainer with ps aux | grep feature-gates

Add HyperV flag on Windows Masters:

  1. Expose the nodes. Go to the portal and to each Windows Node . Then click under network\network interface\ip config\enable public IP address.

  2. Click on Connect on the portal. This will start the RDP session for the node.

  3. Edit the "kubeletstart" script with notepad.exe C:\k\kubeletstart.ps1 (You can click format 'Word Wrap'). Add the --feature-gates=HyperVContainer=true to the flags of 'kubelet.exe'.

  4. Stop kubelet with Stop-Process -Name kubelet.

  5. Restart kubelet with C:\k\kubeletstart.ps1.

  6. Confirm kubelet has the HyperV flag:

    $process = "kubelet.exe"
    >> Get-WmiObject Win32_Process 
    -Filter "name = '$process'" | Select-Object CommandLine | out-string -Width 2000

Confirm HyperV Isolation

Deploy the following hyperv-winserver.yaml file:

kubectl apply -f hyperv-winserver.yaml
apiVersion: v1
kind: Service
metadata:
  name: win-webserver
  labels:
    app: win-webserver
spec:
  ports:
    # the port that this service should serve on
  - port: 80
    targetPort: 80
  selector:
    app: win-webserver
  type: LoadBalancer
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: win-webserver
  name: win-webserver
spec:
  replicas: 1
  template:
    metadata:
      annotations:
        experimental.windows.kubernetes.io/isolation-type: hyperv
      labels:
        app: win-webserver
      name: win-webserver
    spec:
      containers:
      - name: windowswebserver
        image: microsoft/windowsservercore:1709
        command:
        - powershell.exe
        - -command
        - "<#code used from https://gist.github.com/wagnerandrade/5424431#> ; $$listener = New-Object System.Net.HttpListener ; $$listener.Prefixes.Add('http://*:80/') ; $$listener.Start() ; $$callerCounts = @{} ; Write-Host('Listening at http://*:80/') ; while ($$listener.IsListening) { ;$$context = $$listener.GetContext() ;$$requestUrl = $$context.Request.Url ;$$clientIP = $$context.Request.RemoteEndPoint.Address ;$$response = $$context.Response ;Write-Host '' ;Write-Host('> {0}' -f $$requestUrl) ;  ;$$count = 1 ;$$k=$$callerCounts.Get_Item($$clientIP) ;if ($$k -ne $$null) { $$count += $$k } ;$$callerCounts.Set_Item($$clientIP, $$count) ;$$header='<html><body><H1>Windows Container Web Server</H1>' ;$$callerCountsString='' ;$$callerCounts.Keys | % { $$callerCountsString+='<p>IP {0} callerCount {1} ' -f $$_,$$callerCounts.Item($$_) } ;$$footer='</body></html>' ;$$content='{0}{1}{2}' -f $$header,$$callerCountsString,$$footer ;Write-Output $$content ;$$buffer = [System.Text.Encoding]::UTF8.GetBytes($$content) ;$$response.ContentLength64 = $$buffer.Length ;$$response.OutputStream.Write($$buffer, 0, $$buffer.Length) ;$$response.Close() ;$$responseStatus = $$response.StatusCode ;Write-Host('< {0}' -f $$responseStatus)  } ; "
      nodeSelector:
        beta.kubernetes.io/os: windows

RDP into the Windows node that is running your pod. Do a docker ps to identify the container running the server and then run docker inspect <<container_id>> and look for the flag "Isolation": "hyperv", this will confirm that you have a kubernetes cluster scheduling hyperv containers on your Windows nodes. Note: I have only tested this on 1709 sku Standard_D2_v3, and under all the conditions described above.

@feiskyer
Copy link
Member

Another way to set it is kubeletConfig

@brusMX
Copy link
Author

brusMX commented Apr 10, 2018

@feiskyer thanks for the input!
Actually, I have tried it. I provisioned new cluster using the following "hyperv-cluster.json" file:

{
    "apiVersion": "vlabs",
    "properties": {
        "orchestratorProfile": {
            "orchestratorType": "Kubernetes",
            "orchestratorRelease": "1.10",
            "kubernetesConfig": {
               "apiServerConfig" : {
                 "--feature-gates": "HyperVContainer=true"
               },
               "kubeletConfig" : {
                    "--feature-gates": "HyperVContainer=true"
                }
           }
        },
        "masterProfile": {
            "count": 1,
            "dnsPrefix": "hyperv-cluster-012",
            "vmSize": "Standard_DS2_v2"
        },
        "agentPoolProfiles": [
            {
                "name": "linuxpool1",
                "count": 1,
                "vmSize": "Standard_DS2_v2",
                "storageProfile" : "ManagedDisks",
                "availabilityProfile": "AvailabilitySet"
            },
            {
                "name": "windowspool1",
                "count": 1,
                "vmSize": "Standard_D2_v3",
                "availabilityProfile": "AvailabilitySet",
                "osType": "Windows"
            }
        ],
        "windowsProfile": {
            "adminUsername": "azureuser1",
            "adminPassword": "pss10rdw!"
        },
        "linuxProfile": {
            "adminUsername": "azureuser1",
            "ssh": {
                "publicKeys": [
                    {
                        "keyData": ""
                    }
                ]
            }
        },
        "servicePrincipalProfile": {
            "clientId": "",
            "secret": ""
        }
    }
}

And I got excited because I ssh into the master and kubelet was running with the needed hyperv flag.

Nevertheless, when I RDP into the windows node kubelet doesn't have the hyperV flag:

PS C:\Users\azureuser1> $process = "kubelet.exe"
>> Get-WmiObject Win32_Process -Filter "name = '$process'" | Select-Object CommandLine | out-string -Width 2000

CommandLine                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
-----------                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
"C:\k\kubelet.exe" 
--hostname-override=14796k8s9010 
--pod-infra-container-image=kubletwin/pause --resolv-conf= --allow-privileged=true 
--enable-debugging-handlers --cluster-dns=10.0.0.10 --cluster-domain=cluster.local 
--kubeconfig=c:\k\config --hairpin-mode=promiscuous-bridge --v=2 
--azure-container-registry-config=c:\k\azure.json --runtime-request-timeout=10m 
--cloud-provider=azure --cloud-config=c:\k\azure.json --image-pull-progress-deadline=20m 
--cgroups-per-qos=false --enforce-node-allocatable= --volume-plugin-dir=c:\k\volumeplugins 
--network-plugin=cni --cni-bin-dir=c:\k\azurecni\bin --cni-conf-dir=c:\k\azurecni\netconf

At least now, the only thing to do is to fix the way windows is starting kubelet to accept kubeletConfig.

@feiskyer
Copy link
Member

So this is a Windows Kubelet config problem. @JiangtianLi Could you help to fix this issue?

@PatrickLang
Copy link
Contributor

btw - here's my gist with steps to deploy. https://gist.github.com/PatrickLang/2cf4b81c9518e531b828a71e4c430332

Looking forward to kubeletConfig making this easier :)

@brusMX
Copy link
Author

brusMX commented Apr 24, 2018

Thanks for the input @PatrickLang, good idea just replacing the output script before deployment. I am waiting for @jiantianli to roll a fix :)

@JiangtianLi JiangtianLi self-assigned this Apr 24, 2018
@JiangtianLi
Copy link
Contributor

@brusMX Assigned and will work on it. Thanks for reporting.

@PatrickLang
Copy link
Contributor

/assign

@acs-bot
Copy link

acs-bot commented Jun 8, 2018

@PatrickLang: GitHub didn't allow me to assign the following users: PatrickLang.

Note that only Azure members and repo collaborators can be assigned.

In response to this:

/assign

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@PatrickLang
Copy link
Contributor

PatrickLang commented Jun 8, 2018

/assign patricklang

@ams0
Copy link

ams0 commented Aug 2, 2018

I was trying to do this on 1803, and I got:

failed to start sandbox container for pod "win-webserver-5cf8fc655d-zzgqz": Error response from daemon: container de8a54920d130e38608e7c9b8b46ab09c37f9580fcfae94b408ed3bd31b4ed59 encountered an error during CreateContainer: failure in a Windows system call: No hypervisor is present on this system.

Do you know if Hyper-V is disabled on 1803 by default? I can't get it to work even after I give the powershell command:

Install-WindowsFeature -Name Hyper-V -ComputerName 33861acs000000

and reboot (hyperv is already installed).

<>

Ignore the above, was the wrong size! Standard_E8_v3 did the trick

@PatrickLang
Copy link
Contributor

What vm sku did you use? It's only on some sku like dv3 and ev3. Maybe f but I'm not 100%

@ams0
Copy link

ams0 commented Aug 2, 2018

Interestingly, DS3_v2 didn't work, but Ex_v3 does it. Time to write a blog post about this findings..

@paulbouwer
Copy link
Member

@ams0 - You will need one of the nested virtualisation VMs (Dv3 or Ev3) as per the comment by @PatrickLang. A DS3_v2 is not a nested virtualisation capable VM.

Have a look at #3246 - Windows kubelet defaults are mistakenly applied to Linux agents - I think there are still issues running this setup currently.

Nested Virtualisation VMs:

@PatrickLang
Copy link
Contributor

I should have this fixed by end of week now that #3246 is out of the way. I have a branch where I'm refactoring and cleaning up the kubelet parameters - https://github.com/patricklang/acs-engine/tree/patricklang-2627

@ghost ghost removed the in progress label Aug 30, 2018
@PatrickLang
Copy link
Contributor

Windows Server version 1709 pods seem to work ok on 1803, but 2016 is broken.

2 bugs:
kubernetes/kubernetes#68374
kubernetes/kubernetes#68375

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants