Developer Blog

Tipps und Tricks für Entwickler und IT-Interessierte

Kubernetes| Getting started

Readings

TL;DR

Helm (Documentation): Using Helm

choco install kubernetes-helm
helm
helm repo add stable https://charts.helm.sh/stable
helm search repo stable
helm repo update
helm install stable/mysql --generate-name
helm install stable/mysql --generate-name
helm ls
helm status mysql-1609772389
helm search hub wordpress
helm repo add brigade https://brigadecore.github.io/charts
helm repo ls
helm search repo brigade

Customizing the Chart Before Installing

Helm: Concepts

Three Big Concepts

ChartHelm package. It contains all of the resource definitions necessary to run an application, tool, or service inside of a Kubernetes cluster.
RepositoryPlace where charts can be collected and shared.
ReleaseInstance of a chart running in a Kubernetes cluster.
One chart can often be installed many times into the same cluster. And each time it is installed, a new release is created.
Consider a MySQL chart. If you want two databases running in your cluster, you can install that chart twice. Each one will have its own release, which will in turn have its own release name.

Helm: Creating Charts

helm create chartname
helm package chartname
helm install mychart
helm install mychart mychart/ --values mychart/values.yaml
helm ls
helm delete generated-deployment-name
helm package mychart
helm install mychart-0.1.0.tgz
helm install release-name mychart 

Helm: CLI

Common actions for Helm:

- helm search:    search for charts
- helm pull:      download a chart to your local directory to view
- helm install:   upload the chart to Kubernetes
- helm list:      list releases of charts

Environment variables:

| Name                               | Description                                                                       |
|------------------------------------|-----------------------------------------------------------------------------------|
| $HELM_CACHE_HOME                   | set an alternative location for storing cached files.                             |
| $HELM_CONFIG_HOME                  | set an alternative location for storing Helm configuration.                       |
| $HELM_DATA_HOME                    | set an alternative location for storing Helm data.                                |
| $HELM_DEBUG                        | indicate whether or not Helm is running in Debug mode                             |
| $HELM_DRIVER                       | set the backend storage driver. Values are: configmap, secret, memory, postgres   |
| $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use.                      |
| $HELM_MAX_HISTORY                  | set the maximum number of helm release history.                                   |
| $HELM_NAMESPACE                    | set the namespace used for the helm operations.                                   |
| $HELM_NO_PLUGINS                   | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.                        |
| $HELM_PLUGINS                      | set the path to the plugins directory                                             |
| $HELM_REGISTRY_CONFIG              | set the path to the registry config file.                                         |
| $HELM_REPOSITORY_CACHE             | set the path to the repository cache directory                                    |
| $HELM_REPOSITORY_CONFIG            | set the path to the repositories file.                                            |
| $KUBECONFIG                        | set an alternative Kubernetes configuration file (default "~/.kube/config")       |
| $HELM_KUBEAPISERVER                | set the Kubernetes API Server Endpoint for authentication                         |
| $HELM_KUBEASGROUPS                 | set the Groups to use for impersonation using a comma-separated list.             |
| $HELM_KUBEASUSER                   | set the Username to impersonate for the operation.                                |
| $HELM_KUBECONTEXT                  | set the name of the kubeconfig context.                                           |
| $HELM_KUBETOKEN                    | set the Bearer KubeToken used for authentication.                                 |

Helm stores cache, configuration, and data based on the following configuration order:

– If a HELM_*_HOME environment variable is set, it will be used
– Otherwise, on systems supporting the XDG base directory specification, the XDG variables will be used
– When no other location is set a default location will be used based on the operating system

By default, the default directories depend on the Operating System. The defaults are listed below:

| OS      | Cache Path                | Configuration Path             | Data Path
|---------|---------------------------|--------------------------------|-------------------------|
| Linux   | $HOME/.cache/helm         | $HOME/.config/helm             | $HOME/.local/share/helm
| macOS   | $HOME/Library/Caches/helm | $HOME/Library/Preferences/helm | $HOME/Library/helm
| Windows |

Available Commands:

  completion  generate autocompletions script for the specified shell
  create      create a new chart with the given name
  dependency  manage a chart's dependencies
  env         helm client environment information
  get         download extended information of a named release
  help        Help about any command
  history     fetch release history
  install     install a chart
  lint        examine a chart for possible issues
  list        list releases
  package     package a chart directory into a chart archive
  plugin      install, list, or uninstall Helm plugins
  pull        download chart from repository and unpack in local directory
  repo        add, list, remove, update, and index chart repositories
  rollback    roll back a release to a previous revision
  search      search for a keyword in charts
  show        show information of a chart
  status      display the status of the named release
  template    locally render templates
  test        run tests for a release
  uninstall   uninstall a release
  upgrade     upgrade a release
  verify      verify that a chart at the given path has been signed and is valid
  version     print the client version information

Flags

      --debug                       enable verbose output
  -h, --help                        help for helm
      --kube-apiserver string       the address and the port for Kubernetes API server
      --kube-as-group stringArray   Group to impersonate for the operation, 
                                    flag can be repeated to specify multiple groups.
      --kube-as-user string         Username to impersonate for the operation
      --kube-context string         name of the kubeconfig context to use
      --kube-token string           bearer token used for authentication
      --kubeconfig string           path to the kubeconfig file
  -n, --namespace string            namespace scope for this request
      --registry-config string      path to the registry config file 
                                    (<User>\AppData\Roaming\helm\registry.json)
      --repository-cache string     path to file containing cached repository indexes
                                    (<User>\AppData\Local\Temp\helm\repository)
      --repository-config string    path to file containing repository names and URLs
                                   (<User>\AppData\Roaming\helm\repositories.yaml)
 
Use "helm [command] --help" for more information about a command.

General Information

Minikube

Installation

https://minikube.sigs.k8s.io/docs/start/

Start Cluster

minikube start
* minikube v1.16.0 auf Microsoft Windows 10 Pro 10.0.19042 Build 19042
* Automatically selected the docker driver
* Starting control plane node minikube in cluster minikube
* Pulling base image ...
* Downloading Kubernetes v1.20.0 preload ...
    > preloaded-images-k8s-v8-v1....: 491.00 MiB / 491.00 MiB  100.00% 24.50 Mi
* Creating docker container (CPUs=2, Memory=8100MB) ...
E1225 11:29:49.056358   11564 kic.go:241] icacls failed applying permissions - err - [
1 Dateien erfolgreich verarbeitet, bei 0 Dateien ist ein Verarbeitungsfehler aufgetreten.]
* Vorbereiten von Kubernetes v1.20.0 auf Docker 20.10.0...
  - Generating certificates and keys ...
  - Booting up control plane ...
  - Configuring RBAC rules ...
* Verifying Kubernetes components...
* Enabled addons: storage-provisioner, default-storageclass
* Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
kubectl get po -A
minikube kubectl -- get po -A
minikube dashboard
kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.4
kubectl expose deployment hello-minikube --type=NodePort --port=8080
kubectl get services hello-minikube
minikube service hello-minikube

Alternatively, use kubectl to forward the port:

kubectl port-forward service/hello-minikube 7080:8080

Kubernetes Basics Module

Reference

Kubernetes : production-grade, open-source platform that orchestrates the placement (scheduling) and execution of application containers within and across computer clusters.

Deployment: responsible for creating and updating instances of your application

Pod: group of one or more application containers (such as Docker) and includes shared storage (volumes), IP address and information about how to run them.

Node: a worker machine in Kubernetes and may be either a virtual or a physical machine, depending on the cluster. Multiple Pods can run on one Node.

Kubernetes Service: abstraction layer which defines a logical set of Pods and enables external traffic exposure, load balancing and service discovery for those Pods.

Scaling is accomplished by changing the number of replicas in a Deployment

Tutorial

1. Create a Kubernetes cluster

minikube version
minikube start
kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:49153
KubeDNS is running at https://127.0.0.1:49153/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

2. Deploy an App

kubectl get node
NAME       STATUS   ROLES                  AGE     VERSION
minikube   Ready    control-plane,master   5d22h   v1.20.0
kubectl create deployment kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1
deployment.apps/kubernetes-bootcamp created
kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1/1     1            1           52s
kubectl proxy
Starting to serve on 127.0.0.1:8001
curl http://localhost:8001/version
{
  "major": "1",
  "minor": "20",
  "gitVersion": "v1.20.0",
  "gitCommit": "af46c47ce925f4c4ad5cc8d1fca46c7b77d13b38",
  "gitTreeState": "clean",
  "buildDate": "2020-12-08T17:51:19Z",
  "goVersion": "go1.15.5",
  "compiler": "gc",
  "platform": "linux/amd64"
}
kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
hello-minikube-6ddfcc9757-kpl5f        1/1     Running   1          5d22h
kubernetes-bootcamp-57978f5f5d-ccr6p   1/1     Running   0          6m25s
k
ubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{end}}'

3. Explore your app

kubectl get pods
> kubectl describe pods
> kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{end}}'
kubernetes-bootcamp-57978f5f5d-z8vsm
> $PODNAME=kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{end}}
> Invoke-WebRequest http://localhost:8001/api/v1/namespaces/default/pods/$PODNAME
StatusCode        : 200
StatusDescription : OK
Content           : {
                      "kind": "Pod",
                      "apiVersion": "v1",
                      "metadata": {
                        "name": "kubernetes-bootcamp-57978f5f5d-z8vsm",
                        "generateName": "kubernetes-bootcamp-57978f5f5d-",
                        "namespace": "default",
                        "uid…
RawContent        : HTTP/1.1 200 OK
                    Cache-Control: no-cache, private
                    Date: Thu, 31 Dec 2020 10:07:12 GMT
                    X-Kubernetes-Pf-Flowschema-Uid: be79abd4-3f87-4ca2-80be-2b83f4c48148
                    X-Kubernetes-Pf-Prioritylevel-Uid: 3c0f88d…
Headers           : {[Cache-Control, System.String[]], [Date, System.String[]], [X-Kubernetes-Pf-Flowschema-Uid, System.String[]], [X-Kubernetes-Pf-Prioritylevel-Uid, System.String[]]…}
Images            : {}
InputFields       : {}
Links             : {}
RawContentLength  : 5593
RelationLink      : {}
> kubectl logs $PODNAME
Kubernetes Bootcamp App Started At: 2020-12-31T09:09:43.763Z | Running On:  kubernetes-bootcamp-57978f5f5d-z8vsm
> kubectl exec $PODNAME env
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=kubernetes-bootcamp-57978f5f5d-z8vsm
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_PORT=443
NPM_CONFIG_LOGLEVEL=info
NODE_VERSION=6.3.1
HOME=/root
> kubectl exec -ti $PODNAME bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@kubernetes-bootcamp-57978f5f5d-z8vsm:/# exit
exit

4. Expose your app publicly

Step 1 Create a new service
> kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-57978f5f5d-z8vsm   1/1     Running   1          95m
❯ kubectl get services
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP          6d
> kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
service/kubernetes-bootcamp exposed
> kubectl get services
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes            ClusterIP   10.96.0.1       <none>        443/TCP          6d
kubernetes-bootcamp   NodePort    10.98.244.121   <none>        8080:31603/TCP   3m58s
> kubectl describe services/kubernetes-bootcamp
Name:                     kubernetes-bootcamp
Namespace:                default
Labels:                   app=kubernetes-bootcamp
Annotations:              <none>
Selector:                 app=kubernetes-bootcamp
Type:                     NodePort
IP Families:              <none>
IP:                       10.98.244.121
IPs:                      10.98.244.121
Port:                     <unset>  8080/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  31603/TCP
Endpoints:                172.17.0.5:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>
> $NODEPORT=kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}'
❯ $NODEPORT
31603
Step 2: Using labels
> kubectl describe deployment
Name:                   kubernetes-bootcamp
Namespace:              default
CreationTimestamp:      Thu, 31 Dec 2020 09:49:43 +0100
Labels:                 app=kubernetes-bootcamp
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=kubernetes-bootcamp
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=kubernetes-bootcamp
  Containers:
   kubernetes-bootcamp:
    Image:        gcr.io/google-samples/kubernetes-bootcamp:v1
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Progressing    True    NewReplicaSetAvailable
  Available      True    MinimumReplicasAvailable
OldReplicaSets:  <none>
NewReplicaSet:   kubernetes-bootcamp-57978f5f5d (1/1 replicas created)
Events:          <none>
> kubectl get pods -l app=kubernetes-bootcamp
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-57978f5f5d-z8vsm   1/1     Running   1          31h
> kubectl get services -l app=kubernetes-bootcamp
NAME                  TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes-bootcamp   NodePort   10.98.244.121   <none>        8080:31603/TCP   30h
> $POD_NAME=kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{end}}'
❯ $POD_NAME
kubernetes-bootcamp-57978f5f5d-z8vsm
> kubectl label pod $POD_NAME app=v1
> kubectl label pod $POD_NAME app=v1
error: 'app' already has a value (kubernetes-bootcamp), and --overwrite is false
❯ kubectl label pod $POD_NAME app=v1 --overwrite
pod/kubernetes-bootcamp-57978f5f5d-z8vsm labeled
> kubectl get pods -l app=v1
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-57978f5f5d-z8vsm   1/1     Running   1          31h
Step 3: Deleting a service
> kubectl get services
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes            ClusterIP   10.96.0.1       <none>        443/TCP          7d6h
kubernetes-bootcamp   NodePort    10.98.244.121   <none>        8080:31603/TCP   30h
❯ kubectl delete service -l app=kubernetes-bootcamp
service "kubernetes-bootcamp" deleted

Not reachable from outside

> curl $(minikube ip):$NODE_PORT

Reachable from inside

> kubectl exec -ti $POD_NAME -- curl localhost:8080
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-57978f5f5d-z8vsm | v=1

5. Scale up your app

Step 1: Scaling a deployment

To list your deployments 

> kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1/1     1            1           32h
> kubectl get rs
NAME                             DESIRED   CURRENT   READY   AGE
kubernetes-bootcamp-57978f5f5d   1         1         1       32h
> kubectl scale deployments/kubernetes-bootcamp --replicas=4
deployment.apps/kubernetes-bootcamp scaled
> kubectl get rs
NAME                             DESIRED   CURRENT   READY   AGE
kubernetes-bootcamp-57978f5f5d   4         4         4       32h
> kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   4/4     4            4           32h
> kubectl get pods -o wide
NAME                                   READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
kubernetes-bootcamp-57978f5f5d-9fwnz   1/1     Running   0          28m     172.17.0.6   minikube   <none>           <none>
kubernetes-bootcamp-57978f5f5d-dgvm4   1/1     Running   0          6m33s   172.17.0.8   minikube   <none>           <none>
kubernetes-bootcamp-57978f5f5d-qh686   1/1     Running   0          6m33s   172.17.0.9   minikube   <none>           <none>
kubernetes-bootcamp-57978f5f5d-wx9zg   1/1     Running   0          6m33s   172.17.0.7   minikube   <none>           <none>
kubernetes-bootcamp-57978f5f5d-z8vsm   1/1     Running   1          32h     172.17.0.5   minikube   <none>           <none>
> kubectl describe deployments/kubernetes-bootcamp
Step 2: Load Balancing

Create service if desired

> kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
> $NODE_PORT=kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}'
❯ $NODE_PORT
31937
> $NODE_IP=minikube ip
Step 3: Scale Down
> kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   4/4     4            4           32h
❯ kubectl scale deployments/kubernetes-bootcamp --replicas=2
deployment.apps/kubernetes-bootcamp scaled
❯ kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   2/2     2            2           32h
>  kubectl get pods -o wide
NAME                                   READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
kubernetes-bootcamp-57978f5f5d-9fwnz   1/1     Running   0          48m   172.17.0.6   minikube   <none>           <none>
kubernetes-bootcamp-57978f5f5d-wx9zg   1/1     Running   0          26m   172.17.0.7   minikube   <none>           <none>
kubernetes-bootcamp-57978f5f5d-z8vsm   1/1     Running   1          32h   172.17.0.5   minikube   <none>           <none>

6. Update your app

Power Shell | Working with Aliases

Related CommandLets

  • Get-Alias
  • New-Alias
  • Set-Alias
  • Export-Alias
  • Import-Alias 

Display Aliases

Show Aliases

Get-Alias

Show Aliases for a Command

Get-Alias -Definition Get-ChildItem

Export the entire list to a spreadsheet (a CSV file) by using the Export-Alias:

Export-Alias -Path "Alias.csv"

Show only build-in aliases (with property read-only)

Get-Alias | Where-Object {$_.Options -Match "ReadOnly"}

Create Alias

New-Alias -Name D -Value Get-ChildItem -Description "A shortcut to the Get-ChildItem cmdlet"

Create a shortcut ‘c’ for Visual Studio Code ‘code’

Set-Alias -Name c -Value code  

Create more complex aliases using functions

function Start-TheDay {
    start chrome;
    start firefox;
    start code;
    start slack;
}

Set-Alias -Name am -Value Start-TheDay  

Customize prompt

Microsoft Powershell Core – About Prompts

function Prompt(){
   $W = Split-Path -leaf -path (Get-Location)
   $prompt = Write-Prompt "$($env:UserName)@$($env:ComputerName):" -ForegroundColor Green
   $prompt += Write-Prompt $W -ForegroundColor DarkCyan
   $prompt += Write-Prompt '>'
   
   return ' '
}

Get code that sets the prompt

(Get-Command Prompt).ScriptBlock

Samples

function prompt {"PS [$env:COMPUTERNAME]&gt; "}
function prompt {"$(Get-Date)> "}
function prompt {
  $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
  $principal = [Security.Principal.WindowsPrincipal] $identity
  $adminRole = [Security.Principal.WindowsBuiltInRole]::Administrator

  $(if (Test-Path variable:/PSDebugContext) { '[DBG]: ' }
    elseif($principal.IsInRole($adminRole)) { "[ADMIN]: " }
    else { '' }
  ) + 'PS ' + $(Get-Location) +
    $(if ($NestedPromptLevel -ge 1) { '>>' }) + '> '
}
function prompt {
   # The at sign creates an array in case only one history item exists.
   $history = @(Get-History)
   if($history.Count -gt 0)
   {
      $lastItem = $history[$history.Count - 1]
      $lastId = $lastItem.Id
   }

   $nextCommand = $lastId + 1
   $currentDirectory = Get-Location
   "PS: $nextCommand $currentDirectory >"
}
function prompt {
    $color = Get-Random -Min 1 -Max 16
    Write-Host ("PS " + $(Get-Location) +">") -NoNewLine `
     -ForegroundColor $Color
    return " "
}
function Prompt
{
     # Admin ?
     if( (
        New-Object Security.Principal.WindowsPrincipal (
            [Security.Principal.WindowsIdentity]::GetCurrent())
        ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
    {
        # Admin-mark in WindowTitle
        $Host.UI.RawUI.WindowTitle = "[Admin] " + $Host.UI.RawUI.WindowTitlep
        # Admin-mark on prompt
        Write-Host "[" -nonewline -foregroundcolor DarkGray
        Write-Host "Admin" -nonewline -foregroundcolor Red
        Write-Host "] " -nonewline -foregroundcolor DarkGray
    }   
    Write-Host "[" -NoNewline
    Write-Host (Get-Date -Format "HH:mm:ss") -ForegroundColor Gray -NoNewline
    Write-Host "] [" -NoNewline    
    Write-Host "$(((H)[-1].EndExecutionTime - (H)[-1].StartExecutionTime).Milliseconds) ms" -NoNewline -ForegroundColor Gray
    Write-Host "]" -NoNewline
    if(Get-Module Posh-git) {Write-VcsStatus; Write-host ''}
    Write-Host "$($executionContext.SessionState.Path.CurrentLocation.ProviderPath)" -NoNewline
    "> "
}

Docker Aliases

drm

Removes all stopped containers:

function Remove-StoppedContainers {  
    docker container rm $(docker container ls -q)
}
Set-Alias drm  Remove-StoppedContainers  

drmf

Removes all containers, whether they’re running or not:

function Remove-AllContainers {  
    docker container rm -f $(docker container ls -aq)
}
Set-Alias drmf  Remove-AllContainers  

Use with caution

dip

Gets the container’s IP address – pass it a container name or part of the container ID, e.g. dip 02a or dip dbserver:

function Get-ContainerIPAddress {  
    param (
        [string] $id
    )
    & docker inspect --format '{{ .NetworkSettings.Networks.nat.IPAddress }}' $id
}
Set-Alias dip  Get-ContainerIPAddress  

d2h

Adds a container’s IP address to the host’s hosts file, so you can refer to containers by their name on your Docker host, in the same way that containers reach each other by name.

Example – I have a web app which uses a SQL database. In dev and test environments I’ll be running SQL Server in a container for the database. The container is called petshop-db and all the connection strings in the web configuration use petshop-db as the database server name. If I want to run the web app locally, but still use a container for the database I just start the container and run d2h petshop-db. Now my web app uses the container IP from the hosts file, and I can run the whole stack with docker-compose up without changing config.

function Add-ContainerIpToHosts {  
    param (
        [string] $name
    )
    $ip = docker inspect --format '{{ .NetworkSettings.Networks.nat.IPAddress }}' $name
    $newEntry = "$ip  $name  #added by d2h# `r`n"
    $path = 'C:WindowsSystem32driversetchosts'
    $newEntry + (Get-Content $path -Raw) | Set-Content $path
}
Set-Alias d2h  Add-ContainerIpToHosts  

Network Aliases

List Jobs for Port

function List-Jobs-Using-Port {  
    param (
        [string] $port
    )

    Get-Process -Id (Get-NetTCPConnection -LocalPort $port).OwningProcess
}

Top Ten Aliases

Guy’s Top Ten Aliases

  1. Cd: Set-Location
  2. Cls: Clear-Host
  3. Del: Remove-Item
  4. Diff: Compare-Object
  5. Dir: Get-ChildItem
  6. Kill: Stop-Process
  7. Echo: Write-Output
  8. Sort: Sort-Object
  9. Sleep: Start-Sleep
  10. Type: Get-Content

List of Aliases

More to read

https://stackify.com/powershell-commands-every-developer-should-know/

References and Copyright

Aliases and Prompts

https://gist.github.com/sixeyed/c3ae1fd8033b8208ad29458a56856e05

Scrimba: JavaScript Advent Calendar

Solutions for the Scrimba JavaScriptmas Challenge

Day 24: Test Your Agility!

var pushed = false //Has the stop button been pushed - false is default
var targetInt; //The target number to stop the wheel on
var spinningElem = document.getElementById('spinning'); //The spinning number


const maxLoop = 10;
const waitTime = 250;

//event listener
document.getElementById("buttonPressed").addEventListener("click", buttonPressed);

//When the stop button is pushed
function buttonPressed(){
    pushed = true;
}

//set the target Int
function setTargetInt(){
    var targetElem = document.getElementById('targetNum');
    targetInt=Math.floor(Math.random() * (maxLoop + 1))
    targetElem.innerHTML = targetInt;
}

//sleep const
const sleep = (milliseconds) => {
  return new Promise(resolve => setTimeout(resolve, milliseconds))
}


var curr_spin=-1;

//EDIT THIS FUNCTION
const spin = async () => {
    var spinningElem = document.getElementById('spinning');
    for(let spin=0; spin < maxLoop; spin++) {
        spinningElem.innerHTML = spin;
        console.log('spin:', spin, targetInt, curr_spin)

        if (!pushed) {
            //
        } else if (spin == targetInt) {
            console.log('Button pushed and won')
             var result = document.getElementById('result');
            result.innerText = "WON!";
            spin = maxLoop+1;
        } else {
            console.log('Buitton pushed but missed')
            spin = maxLoop+1;
        }
        
        await sleep(waitTime)
    }
}

//main
setTargetInt();
spin();

Day 23: Social media input challenge

<html>
    <head>
        <link rel="stylesheet" href="index.css">
    </head>
    <body>
        <div class="container">
            <textarea type="text" id="tweet" placeholder="Type in the box"></textarea>
            <div id="counterFooter">140/140</div>
            <button id="btn"><h2>Tweet</h2></button>
        </div>
        <script src="index.js"></script>
    </body>
</html>
body{
  background-color: #023F6A;
  font-family: sans-serif;
}
.container{
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
}
textarea{
  width:50%;
  height: 30vh;
  background-color: rgb(21, 32, 43);
  color: #fff;
  border-radius:10px;
}
textarea::placeholder{
    color:#fff;
}
#counterFooter {
  margin-top: 0.2rem;
  font-size:0.8rem;
  color: #fff;
  margin-left:30%;
  font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
button{
  width:50%;
  background-color: rgb(29, 161, 242);
  border-radius: 10px;
  padding: 0 10%;
}
.tweetRed {
    color: red;
}
button h2{
    color: #fff;
}
.buttonDisabled {
   opacity: .5;
   cursor: default;
}
const counter = document.getElementById('counterFooter');
const button = document.getElementById('btn');
function updateState() {
    const nrOfChar = 140 - tweet.value.length;
    
    counter.innerText = nrOfChar + '/140';
    
    (nrOfChar < 20) ? tweet.classList.add("tweetRed") : tweet.classList.remove("tweetRed");    
    (nrOfChar <  0) ? btn.classList.add("buttonDisabled") : btn.classList.remove("buttonDisabled");
}
function handleKeyUp(event) {
    updateState()
}
tweet.addEventListener('keyup', handleKeyUp);
updateState()

Day 22: Extract Matrix Column

function extractMatrixColumn(matrix, column) {
    let extracted = []
    
    matrix.forEach( row => {
        extracted.push(row[column])
    })
    
    return extracted;
}
function extractMatrixColumn(matrix, column) {   
    return matrix.map( row => row[column])
}

Day 21: Sum of 2

function sumOfTwo(nums1, nums2, value) {
    let result = false;
    
    nums1.forEach( num1 => {
        nums2.forEach( num2 => {            
            if ( (num1 + num2) === value) {
                result = true;
            }
        })
    })
    return result;
}

Day 20: Domain Type

function domainType(domains) {
    map = { "org": "organization",
            "com": "commercial", 
            "net": "network",
            "info":  "information"
    }
    
    types=[]    
    
    domains.forEach( (domain) => {
        last = domain.split('.').slice(-1)[0]
        
        types.push(map[last])
    })
    
    return types
}

Day 19: Alpahabet Subsequence

function alphabetSubsequence(str) {
    chars = str.split("")
    
    result=true
    for(pos=1; pos<chars.length; pos++) {       
        result = result && chars[pos-1] < chars[pos] 
    }
    return result
}

Day 18: Array previous less

function arrayPreviousLess(nums) {
    let result = [];
    let temp=[];
    
    for(indx=0; indx < nums.length; indx++) {
        curr = nums[indx];
        
        // Build array with previous values
        temp.push(curr)
        
        // Find previous values less than current value
        mins = temp.filter( (val) => val < curr)
        
        // Return value at most right
        max = (mins.length == 0) ? -1 : max = mins[mins.length-1];
        
        result.push(max);
    }
    
    return result;
}

Day 17: Different symbols naive

function differentSymbolsNaive(str) {
    m={};
    
    n=0;
    str.split("").sort().forEach( (c) => {                
        if (! (c in m)) n++;
        m[c]=1;
    });
        
    return n
}

Day 16: Insert dashes

function insertDashes(arr) {
    return arr.split(" ").map( part =>part.split("").join("-") ).join(" ");
}

Day 15: Javascript Carousel

function getIndexOfCurrent(tags) {
	let result = -1;
	tags.forEach((node, indx) => {
		if (node.classList.contains('curr')) {
			result = indx;
		}
	});
	return result;
}
function move(dir) {
	var tags = Array.prototype.slice.call(
		document.getElementsByClassName('card')
	);
	let curr = getIndexOfCurrent(tags);
	if (dir === 'prev') {
		next = curr === 0 ? 0 : curr - 1;
	}
	if (dir === 'next') {
		next = curr === tags.length - 1 ? tags.length - 1 : curr + 1;
	}
	tags[curr].setAttribute('class', 'card last');
	tags[next].setAttribute('class', 'card curr');
}
<html>
    <head>
        <link rel="stylesheet" href="index.css">
    </head>
    <body>
        
        <div class="container">
            <img src="previous.svg" class="previous" alt="previous image">
            <div class="gallery-wrapper">
                <div class="gallery">
                    <img class="card curr" src="presents.jpg" alt="Christmas Cookies">
                    <img class="card" src="cookies.jpg" alt="Christmas Cookies">
                    <img class="card" src="santa.jpg" alt="Christmas Cookies">
                    <img class="card" src="candycane.jpg" alt="Christmas Cookies">
                    <img class="card" src="reindeer.jpg" alt="Christmas Cookies">
                </div>
            </div>
            <img src="next.svg" class="next" alt="next image">
        
        </div>
        
        <script src="index.pack.js"></script>
    
    </body>
</html>
/*
Thanks to these fine individuals from Unsplash:
https://unsplash.com/photos/AmzKuEnr1VY
https://unsplash.com/photos/eDnJQL21amc
https://unsplash.com/photos/LXy2DOOxESQ
https://unsplash.com/photos/KBKHXjhVQVM
https://unsplash.com/photos/PxM8aeJbzvk
*/
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@600&display=swap');
html,
body {
	margin: 0;
	padding: 0;
	height: 100vh;
	font-family: 'Playfair Display';
	display: grid;
	justify-content: center;
}
img {
	width: 200px;
}
.previous,
.next {
	width: 35px;
	justify-self: center;
	align-self: center;
	cursor: pointer;
}
.previous {
	opacity: 0.3;
}
.container {
	display: grid;
	grid-template-columns: 20% 200px 20%;
	place-content: center;
}
.card {
	width: 100%;
	display: none;
}
.curr {
	display: block;
}
.curr {
	animation-duration: 5s;
	animation-name: slideIn;
}
.last {
	animation-duration: 2s;
	animation-name: slideOut;
}
@keyframes slideOut {
	from { opacity: 1; }
	to   { opacity: 0; }
}
@keyframes slideIn {
	from { opacity: 0; }
	to   { opacity: 1; }
}

Day 14: Maximal Adjacent Difference

    let result=0, max=0, val1=0, val2=0;
    
    for(pos=0; pos < nums.length; pos++) {
        
        [val1, val2 ] = [ nums[pos], nums[pos+1] ];
        max = (val1 > val2) ? (val1 - val2) : (val2 - val1);
        
        if (max > result) {
            result = max;
        }
    }
     
    return result;
}

Day 13: Extract Eacth Kth

function extractEachKth(nums, kth) {
    return nums.filter( (value, index) => (index+1)
}

Day 12: Valid Time

Number.prototype.between = function(min, max) {
  return this > min && this < max;
};
function validTime(str) {
    const parts = str.split(':')
   
    return parseInt(parts[0]).between(0,24) && parseInt(parts[1]).between(0,59);
}

Day 11: Avoid Obstacles

function avoidObstacles(nums) {
    const sorted = nums.sort();
        
    for(num=2; num <= sorted[nums.length-1]; num++) {
        match = nums.filter((val) => (val
        if (match.length === 0) {
            return num;
        }
    }
}

Day 10: Adjacent Elements Product

function adjacentElementsProduct(nums) {
    let result = 0;
    
    for (i1 = 0; i1 < nums.length-1; ++i1) {
        const product=nums[i1]*nums[i1+1];
                 
        if (product > result) {
            result = product;
        }
    }
    
    return result;
}

Day 09: Sum Odd Fibonacci Numbers

function sumOddFibonacciNumbers(num) {
    let fibPrev=1;
    let fib=0;
    let sum=0;
    
    while (fibPrev < num) {       
        [ fibPrev, fib ] = [fib + fibPrev, fibPrev];
        
        if (fib
            sum += fib;
        }
    }
    
    return sum;
}

Day 08: Rolling Dice

<html>
    <head>
        <link rel="stylesheet" href="style.css">
        <script src="index.pack.js"></script>
    </head>
    <body>
        <div class="dice">
            <div class="dot dot_ dot2 dot_ dot4 dot5 dot6"></div>
            <div class="dot dot_ dot_ dot_ dot_ dot_ dot_"></div>
            <div class="dot dot_ dot_ dot3 dot4 dot5 dot6"></div>
            
            <div class="dot dot_ dot_ dot_ dot_ dot_ dot6"></div>
            <div class="dot dot1 dot_ dot3 dot_ dot5 dot_"></div>
            <div class="dot                          dot6"></div>
            
            <div class="dot dot_ dot_ dot3 dot4 dot5 dot6"></div>
            <div class="dot"></div>
            <div class="dot dot_ dot2 dot_ dot4 dot5 dot6"></div>
        </div>
        
    </body>
</html>
function clear_dice() {
    document.querySelectorAll('.dot').forEach( (dot) => dot.style.visibility = "hidden")
}
function roll_dice() {
    const roll = 1 + Math.floor(Math.random() * 6);
    
    console.log(roll);
    clear_dice()
    document.querySelectorAll('.dot'+roll).forEach( (dot) => {
        dot.style.visibility = "visible"
    })
}
clear_dice()
document.getElementsByClassName('dice')[0].addEventListener('click', roll_dice)
body {
    background-color: #AEB8FE;
    display: flex;
}
.dice {
    width: 230px;
    height: 230px;
    border-radius: 10px;
    background-color: #EFE5DC;
    margin: 100px;
    
    display: grid;
    grid-template-columns: repeat(3, 40px);
    gap: 20px;
    grid-auto-rows: minmax(40px, 40px);
}
.dot {
    width: 40px;
    height: 40px;
    border-radius: 15px;
    background-color: black;
    margin: 35px;
    
    visibility: hidden
}
.dot1 { visibility: none; }
.dot2 { visibility: none; }
.dot3 { visibility: none; }
.dot4 { visibility: none; }
.dot5 { visibility: none; }
.dot6 { visibility: none; }

Day 07: Count Vowel Consonant

function countVowelConsonant(str) {
  function reducer(s,c) {
      return s + ("aeiou".includes(c) ? 1 : 2)
  };
    
  return str.split("").reduce(reducer, 0)
}

Day 06: Sort By Length

function sortByLength(strs) {    
    return strs.sort(function(arg1, arg2) { return arg1.length - arg2.length })
}

Day 05: Reverse String

function reverseAString(str) {   
    return str.split("").reverse().join("")
}

Day 04: Century From Year

function centuryFromYear(num) {
    return Math.floor((num + 99) / 100)
}

Day 03: Chunky Monkey

function chunkyMonkey(values, size) {
    result = [];
    pos = 0;
    
    while (pos < values.length) {
        result.push(values.slice(pos, pos+size));
        pos += size;
    }
    
    return result;
}

Day 02: Deposit Profit

function depositProfit(deposit, rate, threshold) {
    let profit = deposit;
    let years = 0
    while (profit < threshold) {
        profit += profit * 0.01 * rate;
        years++;
    }
    
    return years;
}

Day 01: Candies

https://scrimba.com/learn/adventcalendar/note-at-0-00-coef0430d83cd2a72113bbf09

function candies(children, candy) {
    return children * Math.floor(candy / children)//  write code here.
}
Beitragsbild: NodeJS

Node JS | Setup environments

Working with multiple NodeJS Versions

If you plan to work with different NodeJS Version, we suggest to setup the following folder structure. Choose for #YOUR ROOT# whatever you want as your base folder, e.g. D:\Development

Install NodeJS Binary Package (.zip File)

Download and extract the NodeJS Binary Package to your destination folder.

All NodeJS Archives are here:

https://nodejs.org/dist/v${dist_version}/node-v${dist_version}-win-x64.zip"
NameDefinition
Version32 Bit64 Bit
16.14.2https://nodejs.org/dist/v16.14.2/node-v16.14.2-win-x86.ziphttps://nodejs.org/dist/v16.14.2/node-v16.14.2-win-x64.zip
18.0.0https://nodejs.org/dist/v18.0.0/node-v18.0.0-win-x86.ziphttps://nodejs.org/dist/v18.0.0/node-v18.0.0-win-x64.zip

So, NodeJS 18.0.0 Files should be extracted to:

 #YOUR ROOT#\Environments\NodeJS\18.0.0\node

Kepp in mind: if you extract the zip file. it creates a folder with the name of the zipfile and extracts all files in this folder, e.g. node-v18.0.0-win-x86.

We will call this installation folder HOME_NODE.

HOME_NODE = "D:\Development\Environments\NodeJS\18.0.0"

So, best practice is to extract the zip file in the NodeJS folder #YOUR ROOT#\Environments\NodeJS\15.2.1 ($HOME_NODE) and rename node-v15.2.1-win-x86 to node after extraction is complete.

Your finale folder structure should look like this:

Create start script

Create a start script set_env.ps1 to setup up environment variables and path:

Powershell

$NODE_VERSION              = "18.0.0"
$ENV:NODE_HOME             = "D:\Development\Environments\NodeJS\$NODE_VERSION"

$ENV:NPM_CONFIG_USERCONFIG = "$ENV:NODE_HOME\.npmrc"

$ENV:NPM_CONFIG_REGISTRY   = "http://localhost:4873"
$ENV:NPM_CONFIG_PREFIX     = "$ENV:NODE_HOME\npm"


$ENV:OPENSSL_CONF = ''

$ENV:PATH="$ENV:NODE_HOME\node;$ENV:NODE_HOME\npm;$ENV:NODE_HOME\node_modules\npm\bin;$ENV:PATH"

Write-Host "╔═══════════════════════════════════════════════════════════════════════════════════════════════════════╗"
Write-Host "║                                              Node JS                                                  ║"
Write-Host "╚═══════════════════════════════════════════════════════════════════════════════════════════════════════╝"

npm config set registry "$ENV:NPM_CONFIG_REGISTRY"

echo "HOME_NODE           = $ENV:NODE_HOME"
echo "NODE                = $((Get-Command node.exe).Path)"
echo "NPM_CONFIG_PREFIX   = $ENV:NPM_CONFIG_PREFIX"
echo "NPM_CONFIG_REGISTRY = $ENV:NPM_CONFIG_REGISTRY"

Depending on your security settings, you must sign the script.

Read here on how to do this.

Fix for Version 18.x.x

Using the original archive, you may get an error when you run node.exe

To fix this, set the environment variable OPENSSL_CONF (as we already did in set_env.ps1)

$ENV:OPENSSL_CONF = ''

Create a Shortcut

Create a Shortcut for this file by dragging the PowerShell script (with pressed ALT key).

Rename the Shortcut, e.g. “Start NodeJS 18.0.0”

Edit the Shortcut properties

Right-Mouse-Click on Symbol and then select Properties:

Change Destination to:

powershell.exe -noexit D:\Development\Environments\NodeJS\18.0.0\set_env.ps1

Explanation

Call the PowerShell program

powershell.exe

With the parameter

 -noexit

And the parameter

D:\Development\Environments\NodeJS\18.0.0\set_env.ps1

Which means:

Call PowerShell to run the script set_env.ps1 and stay in PowerShell after the Script is finished.

Configure with .npmrc

Create a configuration file .npmrc in the folder $HOME_NODE (e.g., D:\Development\Environments\NodeJS\18.0.0) using your favourite editor.

notepad D:\Development\Environments\NodeJS\18.0.0\.npmrc

Add this line to the file:

cache=D:\Development\Environments\NodeJS\15.2.1\npm\cache
cache-folder=D:\Development\Environments\NodeJS\15.2.1\yarn\cache
global-folder=D:\Development\Environments\NodeJS\15.2.1\yarn
prefix=D:\Development\Environments\NodeJS\15.2.1\npm

Save the file

Run the script

Double-Click on the shortcut

Tipps und Tricks

Install Build Tools

Some packages need to build from source. These packages require additional tool, e.g. compiler.

Install all the required tools and configurations using Microsoft’s windows-build-tools using npm install --global --production windows-build-tools from an elevated PowerShell or CMD.exe (run as Administrator).

npm install --global --production windows-build-tools

ReactJS | Cookbook

General Readings

Create App

Run the following commands to create a new React Native project called “app-getting-started”:

npx create-react-app  app-getting-started
cd app-getting-started

Start App

npm start

Add Typescript Support

yarn create react-app app-getting-started-with-typescript --folder-name
cd app-getting-started-with-typescript
yarn add typescript @types/node @types/react @types/react-dom @types/jest

Testing: Tools and Frameworks

ReactJS related

  • React Testing Utilities – Home
  • Jest – Delightful JavaScript Testing Framework with a focus on simplicity – Home
  • Jasmine – Home | Github
  • Enzyme – Home | Github

Others

ReactJS | Getting Started

Readings

Learning Path

  • Fundamentals Props and State
    • Create React App
    • JSX
    • Function Components
    • Class Components
    • useState and useEffect Hooks
    • setState and Component Lifecycle Methods
    • Conditional Rendering
    • Lists and Keys
    • Building Simple Forms
  • Advanced Topics
    • Hooks
      • useContext
      • useReducer
      • useRef
      • useMemo
      • useCallback
      • Custom Hooks
    • Context
    • Higher Order Components
    • Render Props
    • Refs
    • Error Boundaries
    • Portals
    • HTTP Requests
      • GET
      • POST
  • Ecosystem
    • State Management
      • Redux/ Mobx
      • Apollo Client
    • Routing
      • React Router
    • Styling
      • Styled Components/ Emotion
      • Tailwind CSS
      • Chakra UI / Material UI / Ant Design
    • Forms
      • Formik
      • React Hook Form
    • Testing
      • Jest + React Testing Library
      • Cypress
    • Misc
      • TypeScript
      • React i18Next
      • Storybook
      • Firebase
      • Practical React Libraries
    • Next Steps
      • Gatsby
      • Next.js
      • React Native

Topics

  • Create React App
  • Functional Components
  • Class Components
  • JSX
  • Props And State
  • useState and useEffect Hooks
  • setState and Component Lifecycle Methods
  • Conditional Rendering
  • Lists and Keys
  • Building Simple Forms

Get Started

Create App

Run the following commands to create a new React Native project called “app-getting-started”:

npx create-react-app  app-getting-started
cd app-getting-started

Start App

npm start

Keycloak | Cookbook

Thymeleaf

Add css file

Add css files from static folder into templates files

<link href="../static/lib/bootstrap/current/bootstrap.min.css" th:href="@{/lib/bootstrap/current/bootstrap.min.css}" rel="stylesheet" />

Keycloak | Troubleshooting

Configuration

Change Server Port

By default, executing standalone.sh (for Linux) / standalone.bat (for Windows) makes the server available at following urls:

https://localhost:8443/auth/
http://localhost:8080/auth/

If we need to run multiple instance of server at different ports, run the following command:

For Linux:

standalone.sh -Djboss.socket.binding.port-offset=100

For Windows:

standalone.bat -Djboss.socket.binding.port-offset=100

The above commands will add the offset of 100 to the defaults ports available for Keycloak server.

For example, previously the ports where 8443 and 8000. Now the new ports become 8443 + 100 = 8543 and 8080 + 100 = 8180.

References:

https://www.keycloak.org/docs/3.2/getting_started/topics/secure-jboss-app/before.html

Location of Server Logfile

\standalone\log

Errors

Client secret not provided in request

{"error":"unauthorized_client","error_description":"Client secret not provided in request"

Possible Solution

Change Access Type to public

Keycloak - Set Access Type to PUBLIC

Additional Readings

Keycloak | Getting Started

Introduction

Keycloak – Open Source Identity and Access Management for Modern Applications and Services

Installation

Java

Using AdoptOpenJDK8

brew info adoptopenjdk8

Eclipse

Keycloak

WildFly

See Also

Troubleshooting

Change Server Port

By default, executing standalone.sh (for Linux) / standalone.bat (for Windows) makes the server available at following urls:

https://localhost:8443/auth/
http://localhost:8080/auth/

If we need to run multiple instance of server at different ports, run the following command:

For Linux:

standalone.sh -Djboss.socket.binding.port-offset=100

For Windows:

standalone.bat -Djboss.socket.binding.port-offset=100

The above commands will add the offset of 100 to the defaults ports available for Keycloak server.

For example, previously the ports where 8443 and 8000. Now the new ports become 8443 + 100 = 8543 and 8080 + 100 = 8180.

References:

https://www.keycloak.org/docs/3.2/getting_started/topics/secure-jboss-app/before.html

Location of Server Logfile

\standalone\log

Additional Readings