• GitHub Actions - Kubernetes tools installer

    I’ve published a new GitHub Action called action-setup-kube-tools (View on Marketplace). The action installs Kubernetes tools (kubectl, kustomize, helm, kubeval, conftest, and yq) and cache them on the runner. This is a typescript version of stefanprodan/kube-tools with no command input param.

    Usage

    Inputs

    Parameter Required Default Value Description
    kubectl false 1.18.2 kubectl version. kubectl vesion can be found here
    kustomize false 3.5.5 kustomize version. kustomize vesion can be found here
    helm false 2.16.7 helm version. helm vesion can be found here
    helmv3 false 3.2.1 helm v3 version. helm v3 vesion can be found here
    kubeval false 0.15.0 kubeval version. kubeval vesion can be found here
    conftest false 0.19.0 conftest version. conftest vesion can be found here

    Supported Environments: Linux

    Outputs

    Parameter Description
    kubectl_path kubectl command path
    kustomize_path kustomize command path
    helm_path helm command path
    helmv3_path helm v3 command path
    kubeval_path kubeval command path
    conftest_path conftest command path
    yq_path yq command path

    Sample Workflow

    Specific versions for the commands can be setup by adding inputs parameters like this:

      test: 
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v1
        - uses: yokawasa/action-setup-kube-tools@v0.1.0
          with:
            kubectl: '1.17.1'
            kustomize: '3.7.0'
            helm: '2.16.7'
            helmv3: '3.2.4'
            kubeval: '0.14.0'
            conftest: '0.18.2'
          id: setup
        - run: |
            kubectl=${{steps.setup.outputs.kubectl-path}}
            kustomize=${{steps.setup.outputs.kustomize-path}}
            helm=${{steps.setup.outputs.helm-path}}
            helmv3=${{steps.setup.outputs.helmv3-path}}
            kubeval=${{steps.setup.outputs.kubeval-path}}
            conftest=${{steps.setup.outputs.conftest-path}}
            yq=${{steps.setup.outputs.yq-path}}
    
            ${kubectl} version --client
            ${kustomize} version
            ${helm} version --client
            ${helmv3} version
            ${kubeval} --version
            ${conftest} --version
    

    Default versions for the commands will be setup if you don’t give any inputs like this:

      test: 
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v1
        - uses: yokawasa/action-setup-kube-tools@v0.1.0
          id: setup
        - run: |
            kubectl=${{steps.setup.outputs.kubectl-path}}
            kustomize=${{steps.setup.outputs.kustomize-path}}
            helm=${{steps.setup.outputs.helm-path}}
            helmv3=${{steps.setup.outputs.helmv3-path}}
            kubeval=${{steps.setup.outputs.kubeval-path}}
            conftest=${{steps.setup.outputs.conftest-path}}
            yq=${{steps.setup.outputs.yq-path}}
    
            ${kubectl} version --client
            ${kustomize} version
            ${helm} version --client
            ${helmv3} version
            ${kubeval} --version
            ${conftest} --version
    

    Developing the action

    Install the dependencies

    npm install
    

    Build the typescript and package it for distribution by running ncc

    npm run build && npm run pack
    

    Finally push the resutls

    git add dist
    git commit -a -m "prod dependencies"
    git push origin releases/v0.1.0
    

    Enjoy the action!

  • GitHub Actions - Elastic Cloud Control (ecctl) tool installer

    I’ve published a new GitHub Action called action-setup-ecctl (View on Marketplace). The action installs a specific version of ecctl (Elastic Cloud control tool) and cache it on the runner.

    Usage

    Inputs

    Parameter Required Default Value Description
    version false latest Ecctl tool version such as v1.0.0-beta3. Ecctl vesion can be found here.

    Supported Environments: Linux and macOS

    Sample Workflow

    A specific version of ecctl can be setup by giving an input - version like this:

    - uses: yokawasa/action-setup-ecctl@v0.1.0
      with:
        version: 'v1.0.0-beta3'   # default is 'latest'
      id: setup
    - run: |
      ecctl=${{steps.setup.outputs.ecctl-path}}
      ${ecctl} version
    

    The latest version of ecctl will be setup if you don’t give an input like this:

    - uses: yokawasa/action-setup-ecctl@v0.1.0
      id: setup
    - run: |
      ecctl=${{steps.setup.outputs.ecctl-path}}
      ${ecctl} version
    

    Developing the action

    Install the dependencies

    npm install
    

    Build the typescript and package it for distribution by running ncc

    npm run build && npm run pack
    

    Finally push the resutls

    git add dist
    git commit -a -m "prod dependencies"
    git push origin releases/v0.1.0
    

    Enjoy the action!

  • my old college roommate

    A scene at wedding ceremony of my old collage roommate

    Jan 18, 2020 at Brooklyn NY

  • Getting Kubernetes Metrics using kubectl

    There are various ways to obtain Kubernetes metrics which you can use to visualize, monitor and alert on your metrics. In reality you most likely use managed or unmanaged OSS metrics collection and visualization tools, but in this post, I introduce how to get raw Kubernetes metrics using kubectl.

    Kubernetes Raw Metrics via Prometheus metrics endpoint

    Many Kubernetes components exposes their metrics via the /metrics endpoint, including API server, etcd and many other add-ons. These metrics are in Prometheus format, and can be defined and exposed using Prometheus client libs

    kubectl get --raw /metrics
    

    Sample output - /metrics

    APIServiceOpenAPIAggregationControllerQueue1_adds 282663
    APIServiceOpenAPIAggregationControllerQueue1_depth 0
    APIServiceOpenAPIAggregationControllerQueue1_longest_running_processor_microseconds 0
    APIServiceOpenAPIAggregationControllerQueue1_queue_latency{quantile="0.5"} 63
    APIServiceOpenAPIAggregationControllerQueue1_queue_latency{quantile="0.9"} 105
    APIServiceOpenAPIAggregationControllerQueue1_queue_latency{quantile="0.99"} 126
    APIServiceOpenAPIAggregationControllerQueue1_queue_latency_sum 1.4331448e+07
    APIServiceOpenAPIAggregationControllerQueue1_queue_latency_count 282663
    APIServiceOpenAPIAggregationControllerQueue1_retries 282861
    APIServiceOpenAPIAggregationControllerQueue1_unfinished_work_seconds 0
    APIServiceOpenAPIAggregationControllerQueue1_work_duration{quantile="0.5"} 59
    APIServiceOpenAPIAggregationControllerQueue1_work_duration{quantile="0.9"} 98
    APIServiceOpenAPIAggregationControllerQueue1_work_duration{quantile="0.99"} 2003
    APIServiceOpenAPIAggregationControllerQueue1_work_duration_sum 2.1373689e+07
    APIServiceOpenAPIAggregationControllerQueue1_work_duration_count 282663
    ...
    

    Kubernetes Raw Metrics via metrics API

    You can access Kubernetes Metrics API via kubectl proxy like this:

    kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes
    kubectl get --raw /apis/metrics.k8s.io/v1beta1/pods
    kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes/<node-name>
    kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/<namespace-name>/pods/<pod-name>
    

    (ref: feiskyer/kubernetes-handbook)

    Outputs via metrics API are unformated JSON, thus it’s good to use jq to parse it.

    Sample output - /apis/metrics.k8s.io/v1beta1/nodes

    kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes | jq
    
    {
      "kind": "NodeMetricsList",
      "apiVersion": "metrics.k8s.io/v1beta1",
      "metadata": {
        "selfLink": "/apis/metrics.k8s.io/v1beta1/nodes"
      },
      "items": [
        {
          "metadata": {
            "name": "ip-xxxxxxxxxx.ap-northeast-1.compute.internal",
            "selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/ip-xxxxxxxxxx.ap-northeast-1.compute.internal",
            "creationTimestamp": "2020-05-24T01:29:05Z"
          },
          "timestamp": "2020-05-24T01:28:58Z",
          "window": "30s",
          "usage": {
            "cpu": "105698348n",
            "memory": "819184Ki"
          }
        },
        {
          "metadata": {
            "name": "ip-yyyyyyyyyy.ap-northeast-1.compute.internal",
            "selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/ip-yyyyyyyyyy.ap-northeast-1.compute.internal",
            "creationTimestamp": "2020-05-24T01:29:05Z"
          },
          "timestamp": "2020-05-24T01:29:01Z",
          "window": "30s",
          "usage": {
            "cpu": "71606060n",
            "memory": "678944Ki"
          }
        }
      ]
    }
    

    Sample output - /apis/metrics.k8s.io/v1beta1/namespaces/NAMESPACE/pods/PODNAME

    kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/cluster-autoscaler-7d8d69668c-5rcmt | jq
    
    {
      "kind": "PodMetrics",
      "apiVersion": "metrics.k8s.io/v1beta1",
      "metadata": {
        "name": "cluster-autoscaler-7d8d69668c-5rcmt",
        "namespace": "kube-system",
        "selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/cluster-autoscaler-7d8d69668c-5rcmt",
        "creationTimestamp": "2020-05-24T01:33:17Z"
      },
      "timestamp": "2020-05-24T01:32:58Z",
      "window": "30s",
      "containers": [
        {
          "name": "cluster-autoscaler",
          "usage": {
            "cpu": "1030416n",
            "memory": "27784Ki"
          }
        }
      ]
    }
    

    See also

  • Visual Studio Code Textbook for programmer was published!

    A book, I co-authored with @hoisjp and @_Dr_ASA, was recently published. Today I went to a bookstore to see if it was actually displayed. It was real!! 🎉🎉🎉

    『プログラマーのためのVisual Studio Codeの教科書』(マイナビ出版)/ Visual Studio Code textbooks for programmers

  • GitHub Actions - SQLCheck Action

    I’ve published a new GitHub Action called SQLCheck Action (View on Marketplace). The action automatically identifies anti-patterns in SQL queries using sqlcheck when PR is requested and comment on the PR if risks are found in the queries.

    Usage

    Supports pull_request event type.

    Inputs

    Parameter Required Default Value Description
    post-comment false true Post comment to PR if it’s true
    token true ”” GitHub Token in order to add comment to PR
    risk-level false 3 Set of SQL anti-patterns to check: 1,2, or 3
    - 1 (all anti-patterns, default)
    - 2 (only medium and high risk anti-patterns)
    - 3 (only high risk anti-patterns)
    verbose false false Add verbose warnings to SQLCheck analysis result
    postfixes false “sql” List of file postfix to match ( separator: comma )

    Sample Workflow

    .github/workflows/test.yml

    name: sqlcheck workflow
    on: pull_request
    
    jobs:
      sqlcheck:
        name: sqlcheck job 
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@master
        - uses: yokawasa/action-sqlcheck@v1.2.1
          with:
            post-comment: true
            risk-level: 3
            verbose: false
            token: ${{ secrets.GITHUB_TOKEN }}
    

    Enjoy the action!

  • My last day at Microsoft

    Today is my last day at Microsoft. About 9 years and 2 months have passed since I joined MS. I’ve had wonderful years filled with various experiences on business.

    In the first 4 years, as FAST Search engineer & consultant, I specialized in Fast search and provided customers in Japan and sometimes in South East Asia & South Korea area with proactive consulting & support. For the next 3 years after that, as a part of Azure Solution Architect teams, I provided architectural guidance & design session, PoC and prototyping support, knowledge transfer trainings with Japanease enterprise customers, collaborating with Azure engineering teams, Azure sales teams and many other stakeholders. I also engaged with communities inside/outside Microsoft, participating in Microsoft hosting conferences / industry showcase events, and contributing OSS communities. In the latest 2 years, as a part of global technical SWAT team in enterprise sales organization internally called ‘Global Black Belt, I was responsible for leading technical engagements to win based on solutions leveraging the Microsoft Azure and OSS with a specialization in cloud native application architecture and development such as Microservices, Containers/Serverless, PaaS, DevOps, and so on. Along with my everyday job I had awesome opportunities to speak at conferences and meetups about many technical topics mostly on Cloud Native tech and architectures. It was awesome 9 years and 2 months!!

    I would like to extend my sincerest thanks to all the my customers and partners I have worked with so far. I also thank, each one of my colleague and managers who made my journey a memorable one in Microsoft. 

    I will start a new phase of my career from tomorrow. But it is indeed a small world in our industry. I hope our paths cross again, and it would be great if we may be able to work together again somewhere. 

    Please reach out to me if you ever want to catch up.
    My personal contacts: yokawasa @ linkedin, twitter, facebook & github

  • Find the lowest latency Azure regions from your place using azping

    azping is a command line tools that help you find the lowest latency Azure Region from your place. It acutally reports median latency to Azure regions. It is a fork of gcping.

    What does azping actually evalulate?

    azping evalulate the median latecy of http requests to Azure blob storage endpoints located in each of Azure reagions from your place. Number of requests to be made to each region is 5 by default, but it can be changed with -n parameter that you can give in executing azping command.

    Here is a list of Azure blob storage endpoints that azping evalulates:

    region endpoint
    eastasia azpingeastasia.blob.core.windows.net/ping
    southeastasia azpingsoutheastasia.blob.core.windows.net/ping
    centralus azpingcentralus.blob.core.windows.net/ping
    eastus azpingeastus.blob.core.windows.net/ping
    eastus2 azpingeastus2.blob.core.windows.net/ping
    westus azpingwestus.blob.core.windows.net/ping
    northcentralus azpingnorthcentralus.blob.core.windows.net/ping
    southcentralus azpingsouthcentralus.blob.core.windows.net/ping
    northeurope azpingnortheurope.blob.core.windows.net/ping
    westeurope azpingwesteurope.blob.core.windows.net/ping
    japanwest azpingjapanwest.blob.core.windows.net/ping
    japaneast azpingjapaneast.blob.core.windows.net/ping
    brazilsouth azpingbrazilsouth.blob.core.windows.net/ping
    australiaeast azpingaustraliaeast.blob.core.windows.net/ping
    australiasoutheast azpingaustraliasoutheast.blob.core.windows.net/ping
    southindia azpingsouthindia.blob.core.windows.net/ping
    centralindia azpingcentralindia.blob.core.windows.net/ping
    westindia azpingwestindia.blob.core.windows.net/ping
    canadacentral azpingcanadacentral.blob.core.windows.net/ping
    canadaeast azpingcanadaeast.blob.core.windows.net/ping
    uksouth azpinguksouth.blob.core.windows.net/ping
    ukwest azpingukwest.blob.core.windows.net/ping
    westcentralus azpingwestcentralus.blob.core.windows.net/ping
    westus2 azpingwestus2.blob.core.windows.net/ping
    koreacentral azpingkoreacentral.blob.core.windows.net/ping
    koreasouth azpingkoreasouth.blob.core.windows.net/ping
    francecentral azpingfrancecentral.blob.core.windows.net/ping
    australiacentral azpingaustraliacentral.blob.core.windows.net/ping
    uaenorth azpinguaenorth.blob.core.windows.net/ping
    southafricanorth azpingsouthafricanorth.blob.core.windows.net/ping

    NOTE All blob storage endpoints are created with the following scripts (Just in case I leave the procedures):

    $ git clone https://github.com/yokawasa/azping.git
    $ cd setup
    # Edit RESOURCE_GROUP and REGION_LIST variables in env.sh
    $ vi env.sh
    # Read variables as enviroment variables
    $ source env.sh
    # Execute the following script that execute the following|
    # (1) Create resource group for azping
    # (2) Create blob storage accounts in each of Azure regions
    # (3) Create $root container and upload ping file to the container
    # (4) Check accessibility to all blob storage endpoints
    $ ping-entrypoint.sh
    

    How to install

    Linux 64-bit|https://azpingrelease.blob.core.windows.net/azping_linux_amd64

    $ curl https://azpingrelease.blob.core.windows.net/azping_linux_amd64 > azping && chmod +x azping
    

    Mac 64-bit|https://azpingrelease.blob.core.windows.net/azping_darwin_amd64

    $ curl https://azpingrelease.blob.core.windows.net/azping_darwin_amd64 > azping && chmod +x azping
    

    Windows 64-bit|https://azpingrelease.blob.core.windows.net/azping_windows_amd64

    # use WSL-bash
    $ curl https://azpingrelease.blob.core.windows.net/azping_windows_amd64 > azping && chmod +x azping
    

    Or, you can always build the binary from the source code like this:

    $ git clone https://github.com/yokawasa/azping.git
    $ cd azping
    $ make
    $ tree bin
    
    bin
    ├── azping_darwin_amd64
    ├── azping_linux_amd64
    └── azping_windows_amd64
    

    Usage

    $ azping [options...]
    
    Options:
    -n   Number of requests to be made to each region.
         By default 5; can't be negative.
    -c   Max number of requests to be made at any time.
         By default 10; can't be negative or zero.
    -t   Timeout. By default, no timeout.
         Examples|"500ms", "1s", "1s500ms".
    -top If true, only the top region is printed.
    
    -csv CSV output; disables verbose output.
    -v   Verbose output.
    

    Screenshot

    Here is an output of azping when I executed azping from my home in Tokyo

    Enjoy azping!

  • Unofficial tips for CKA and CKAD exams

    I’ve taken both Certified Kubernetes Administrator (CKA) and Certified Kubernetes Application Developer (CKAD) exams in the past 2 weeks and fortunately passed both. This is a blog article on tips for both exams based on my experiences.

    About the exam and its curriculum

    CKA focus on managing and operating kubernetes cluster including troubleshooting while CKAD forcus on managing and deploying applicaionts to kubernetes cluster.

    It’s very important to read and understand the exam curriculum and their relevant pages on kubernetes.io as all topics you’re expected to understand are inlcuded in the curriculum and they are definitly going to be on the exam.

    Tips for the exams

    • Read and understand the exam curriculum and their relevant pages on kubernetes.io. You are allowed to look up the following sites during the exam
    • For both CKA and CKAD: the following are books that I found useful for CKA and CKAD. They are defenitly helpful to understand how to manage and operate the kubernetes cluster. Pick one or two that you think good fit and read through:
    • For CKA: learn how to install, configure & validate kubernetes cluster. These are good materials to go through:
    • For CKA: learn how to troubleshoot the cluster. I recommend to read the following pages in kubernetes.io
    • For CKA: these below are Github repo that I though very useful for CKA exam preps:
    • For CKAD: these below are Github repo that I thought very very useful for CKAD exam preps:
    • For both: it’s very important to know a quick way to achieve goals by using kubectl. You can lookup kubernetes.io during the exam but it’s always good to know shortcut ways as the exam time is limited (CKA = 3hrs / CKAD = 2hrs)
      • Kubectl Cheat Sheet
      • Creating template YAML using kubectl dry-run and edit it instead of creating YAML from scratch
        # Pod template
        $ kubectl run nginx --image=nginx --restart=Never --dry-run -o yaml
        # Deployment template
        $ kubectl run nginx --image=nginx  --dry-run -o yaml
        # Servcie template
        $ kubectl expose deployment nginx --type=NodePort --port 8080 --dry-run -o yaml
        
        # [NOTE1]
        # You can change resource to release by changing --restart option:
        # kind       |  option
        # ---------------------------------------------
        # deployment |  none
        # pod        |  --restart=Never
        # job        |  --restart=OnFailure
        # cronjob    |  --schedule='cron format(0/5 * * * ? など)'
        
        # [NOTE2]
        # In case that kubectl run will be deprecated in the future, 
        $ kubectl create deployment nginx --image=nginx --dry-run -o yaml
        
    • Tips during the exam
      • ID: Candidates are required to provide government-issued photo identification before the Exam (Read Candidate Handbook). For non-english language folks, Passport would be the best!
      • Testing Room: you need to prepare a room for testing which must be quiet, private and well lit.
      • Virtual screen: you can virtual screen like tmux and screen. It would be very helpful to use multple screens like using one for deploying kuberenetes resources while using the other one for looking up kuberentes resource state and logs. For example, I’m a tmux user, and added the following .tmux.conf before starting tackling with questions:
        set-option -g prefix C-z
        
      • Command alias: Setting up alias definitly help you to shorten the time you need to type in commands. For examples,
        alias k=kubectl
        
      • Time management: Don’t spend too much time on difficult questions. Try to finish all easy questions first and spend the rest for difficult ones. You don’t have to beat everything to pass the exam!! - A score of at least 74% for CKA and 66% for CKAD to pass the exam
      • Searchability: you can use and search the kubernetes.io pages. Even if you have no idea on questions on the exam, don’t give up and try to type some keywords to search the kubernetes.io pages. You may be lucky enough to find some hints or right answers on the site.

    Final comments

    Again, the most important tip for both exams is to read and understand the exam curriculum and all relevant pages on kubernetes.io. You don’t have to memorize but understand them. Event if you don’t come up with exact command options or kubernetes resource objects for questions in the exam, you can lookup and point to appropriate pages during the exam. In addition, get familiar with basic linux commands and cluster operations and management commands. If you have any questions, send DM to me @yokawasa.

    Good luck with your exams!

    ps. Here are my Certificate IDs

  • Easy way to SSH into Azure Kubernetes Service cluster node VMs

    This is an article on how you can SSH into Azure Kubernetes Service (AKS) cluster node VMs using kubectl-plugin-ssh-jump.

    Motivation

    I wanted to SSH into Azure Kubernetes Service (AKS) cluster node VMs, then looking up azure docs I found a relevant page - Connect with SSH to Azure Kubernetes Service (AKS) cluster nodes for maintenance or troubleshooting. But when I first saw this procedure, I thought this was very troublesome. Lazy person like me couldn’t accept going thourgh all the steps just to SSH into AKS cluster nodes VMs. This is why I decided to create kubectl-plugin-ssh-jump, a kubectl plugin to SSH into Kubernetes nodes using a SSH jump host Pod.

    AKS node access control and SSH access strategy

    By default, AKS nodes are not exposed to the internet, and they are completely isolated to their own virtual network. This is why I took a strategy to go through a jump host Pod to connect to the Kubernetes nodes. I believe this is pretty a general strategy in dealing with firewalling, access privileges, etc.

    Regarding SSH key with which you access the nodes via SSH, you need to use the SSH key that you chosen in creating your AKS cluster. Or in the case that you lose the key, you can update SSH Key by using az command like this (See this for more detail):

    az vm user update \
      --resource-group myResourceGroup \
      --name myVM \
      --username azureuser \
      --ssh-key-value ~/.ssh/id_rsa.pub
    

    As an additional security enhancement, you can configure Access Control(IAM) on the AKS cluster to set permissions on the nodes on who can access them. Please see What is role-based access control (RBAC)? for the detail on RBAC.

    SSH into AKS node VMs using kubectl-plugin-ssh-jump

    Pre-requistes

    This plugin needs the following programs:

    • ssh(1)
    • ssh-agent(1)

    Installation

    Install through krew

    This is a way to install kubectl-ssh-jump through krew. After installing krew by following this, you can install kubectl-ssh-jump like this:

    $ kubectl krew install ssh-jump
    

    Expected output would be like this:

    Updated the local copy of plugin index.
    Installing plugin: ssh-jump
    CAVEATS:
    \
     |  This plugin needs the following programs:
     |  * ssh(1)
     |  * ssh-agent(1)
     |
     |  Please follow the documentation: https://github.com/yokawasa/kubectl-plugin-ssh-jump
    /
    Installed plugin: ssh-jump
    

    Once it’s installed, run:

    $ kubectl plugin list
    
    The following kubectl-compatible plugins are available:
    
    /Users/yoichika/.krew/bin/kubectl-krew
    /Users/yoichika/.krew/bin/kubectl-ssh_jump
    
    $ kubectl ssh-jump
    

    Manual Installation

    Install the plugin by copying the script in the $PATH of your shell.

    # Get source
    $ git clone https://github.com/yokawasa/kubectl-plugin-ssh-jump.git
    $ cd kubectl-plugin-ssh-jump
    $ chmod +x kubectl-ssh-jump
    # Add kubeclt-ssh-jump to the install path.
    $ sudo cp -p kubectl-ssh-jump /usr/local/bin
    

    Once in the $PATH, run:

    $ kubectl plugin list
    
    The following kubectl-compatible plugins are available:
    /usr/local/bin/kubectl-ssh-jump
    
    $ kubectl ssh-jump
    

    How to use

    Usage

    Usage:
      kubectl ssh-jump <dest_node> [options]
    
    Options:
      <dest_node>                     Destination node IP
      -u, --user <sshuser>            SSH User name
      -i, --identity <identity_file>  Identity key file
      -p, --pubkey <pub_key_file>     Public key file
      --skip-agent                    Skip automatically starting SSH agent and adding
                                      SSH Identity key into the agent before SSH login
                                      (=> You need to manage SSH agent by yourself)
      --cleanup-agent                 Clearning up SSH agent at the end
                                      The agent is NOT cleaned up in case that 
                                      --skip-agent option is given
      --cleanup-jump                  Clearning up sshjump pod at the end
                                      Default: Skip cleaning up sshjump pod
      -h, --help                      Show this message
    
    Option parameters Cache

    username, identity, pubkey options are cached, therefore you can omit these options afterward. The options are stored in a file named $HOME/.kube/kubectlssh/options

    $ cat $HOME/.kube/kubectlssh/options
    sshuser=azureuser
    identity=/Users/yokawasa/.ssh/id_rsa_k8s
    pubkey=/Users/yokawasa/.ssh/id_rsa_k8s.pub
    
    SSH Agent (ssh-agent)

    The plugin automatically check if there are any ssh-agents started running by the plugin, and starts ssh-agentif it doesn’t find any ssh-agent running and adds SSH Identity key into the agent before SSH login. If the command find that ssh-agent is already running, it doesn’t start a new agent, and re-use the agent. Add --cleanup-agent option if you want to kill the created agent at the end of command.

    In addtion, add --skip-agent option if you want to skip automatic starting ssh-agent. This is actually a case where you already have ssh-agent managed or you want to manually start the agent.

    Examples

    Show all node list. Simply executing kubectl ssh-jump gives you the list of destination nodes as well as command usage

    $ kubectl ssh-jump
    
    Usage:
      kubectl ssh-jump <dest_node> [options]
    
    Options:
      <dest_node>                     Destination node IP
      -u, --user <sshuser>            SSH User name
      -i, --identity <identity_file>  Identity key file
      -p, --pubkey <pub_key_file>     Public key file
      --skip-agent                    Skip automatically starting SSH agent and adding
                                      SSH Identity key into the agent before SSH login
                                      (=> You need to manage SSH agent by yourself)
      --cleanup-agent                 Clearning up SSH agent at the end
                                      The agent is NOT cleaned up in case that
                                      --skip-agent option is given
      --cleanup-jump                  Clearning up sshjump pod at the end
                                      Default: Skip cleaning up sshjump pod
      -h, --help                      Show this message
    
    Example:
       ....
    
    List of destination node...
    Hostname
    aks-nodepool1-18558189-0
    aks-nodepool1-18558189-1
    aks-nodepool1-18558189-2
    

    Then, SSH into a node aks-nodepool1-18558189-0 with options like:

    • usernaem: azureuser
    • identity:~/.ssh/id_rsa_k8s
    • pubkey:~/.ssh/id_rsa_k8s.pub)
      $ kubectl ssh-jump aks-nodepool1-18558189-0 \
      -u azureuser -i ~/.ssh/id_rsa_k8s -p ~/.ssh/id_rsa_k8s.pub
      

    As explained in usage secion, username, identity, pubkey options are cached, therefore you can omit these options afterward.

    $ kubectl ssh-jump aks-nodepool1-18558189-0
    

    You can pass the commands to run in the destination node like this (Suppose that username, identity, pubkey options are cached):

    echo "uname -a" | kubectl ssh-jump aks-nodepool1-18558189-0
    
    (Output)
    Linux aks-nodepool1-18558189-0 4.15.0-1035-azure #36~16.04.1-Ubuntu SMP Fri Nov 30 15:25:49 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
    

    You can clean up sshjump pod at the end of the command with --cleanup-jump option, otherwise, the sshjump pod stay running by default.

    $ kubectl ssh-jump aks-nodepool1-18558189-0 \
      -u azureuser -i ~/.ssh/id_rsa_k8s -p ~/.ssh/id_rsa_k8s.pub \
      --cleanup-jump
    

    You can clean up ssh-agent at the end of the command with --cleanup-agent option, otherwise, the ssh-agent process stay running once it’s started.

    $ kubectl ssh-jump aks-nodepool1-18558189-0 \
      -u azureuser -i ~/.ssh/id_rsa_k8s -p ~/.ssh/id_rsa_k8s.pub \
      --cleanup-agent
    

    You can skip starting ssh-agent by giving --skip-agent. This is actually a case where you already have ssh-agent managed. Or you can start new ssh-agent and add an identity key to the ssh-agent like this:

    # Start ssh-agent manually
    $ eval `ssh-agent`
    # Add an arbitrary private key, give the path of the key file as an argument to ssh-add
    $ ssh-add ~/.ssh/id_rsa_k8s
    # Then, run the plugin with --skip-agent
    $ kubectl ssh-jump aks-nodepool1-18558189-0 \
      -u azureuser -i ~/.ssh/id_rsa_k8s -p ~/.ssh/id_rsa_k8s.pub \
      --skip-agent
    
    # At the end, run this if you want to kill the current agent
    $ ssh-agent -k
    
  • Set of Envoy Proxy features demos

    Set of demos to demonstrate Envoy Proxy features

    HTTP Routing: Simple Match Routing

    All traffic is routed by the front envoy to the service containers. Internally the traffic is routed to the service envoys, then the service envoys route the request to the flask app via the loopback address. In this demo, all traffic is routed to the service envoys like this:

    • A request (path /service/blue & port 8000) is routed to service_blue
    • A request (path /service/green & port 8000) is routed to service_green
    • A request (path /service/red & port 8000) is routed to service_red

    Key definition 1 - virtual_hosts in front-envoy.yaml

        virtual_hosts:
        - name: backend
            domains:
            - "*"
            routes:
            - match:
                prefix: "/service/blue"
            route:
                cluster: service_green
            - match:
                prefix: "/service/blue"
            route:
                cluster: service_blue
            - match:
                prefix: "/service/red"
            route:
                cluster: service_red
    

    Key definition 2 - clusters in front-envoy.yaml

      clusters:
      - name: service_blue
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        hosts:
        - socket_address:
            address: service_blue
            port_value: 80
      - name: service_green
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        hosts:
        - socket_address:
            address: service_green
            port_value: 80
      - name: service_red
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        hosts:
        - socket_address:
            address: service_red
            port_value: 80
    

    To continue, go to github

    HTTP Routing: Routing based on Header Condition

    Key definition 1 - virtual_hosts in front-envoy.yaml

        virtual_hosts:
        - name: backend
            domains:
            - "*"
            routes:
            - match:
                prefix: "/service/blue"
                headers:
                - name: "x-canary-version"
                    exact_match: "service_green"
            route:
                cluster: service_green
            - match:
                prefix: "/service/blue"
            route:
                cluster: service_blue
            - match:
                prefix: "/service/red"
            route:
                cluster: service_red
    

    Key definition 2 - clusters in front-envoy.yaml

      clusters:
      - name: service_blue
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        hosts:
        - socket_address:
            address: service_blue
            port_value: 80
      - name: service_green
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        hosts:
        - socket_address:
            address: service_green
            port_value: 80
      - name: service_red
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        hosts:
        - socket_address:
            address: service_red
            port_value: 80
    

    To continue, go to github

    HTTP Routing: Blue Green Traffic Splitting

    Key definition 1 - virtual_hosts in front-envoy.yaml

        virtual_hosts:
        - name: backend
            domains:
            - "*"
            routes:
            - match:
                prefix: "/service/blue"
            route:
                weighted_clusters:
                clusters:
                - name: service_green
                    weight: 10
                - name: service_blue
                    weight: 90
            - match:
                prefix: "/service/red"
            route:
                cluster: service_red
    

    Key definition 2 - clusters in front-envoy.yaml

      clusters:
      - name: service_blue
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        hosts:
        - socket_address:
            address: service_blue
            port_value: 80
      - name: service_green
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        hosts:
        - socket_address:
            address: service_green
            port_value: 80
      - name: service_red
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        hosts:
        - socket_address:
            address: service_red
            port_value: 80
    

    To continue, go to github

    Fault Injection

    Fault injection is a technique for improving the coverage of a test by introducing faults to test code paths, in particular error handling code paths, that might otherwise rarely be followed. The filter ( http_filters in the demo ) can be used to inject delays and abort requests with user-specified error codes.

    Front-proxy configurations are the same as the ones in HTTP Routing: Simple Match Routing. Differences from HTTP Routing: Simple Match Routing are the following 2 fault injections:

    • Fault injection abort (50% of requests will be aborted with 503 error code) for the requests to service_red
    • Fault injection delay (50% of requests will be 10 seconds delayed) for the requests to service_blue

    Key definition 1 - http_filters in service-envoy-fault-injection-abort.yaml

        http_filters:
        - name: envoy.fault
        config:
            abort:
            http_status: 503
            percentage:
                numerator: 50
                denominator: HUNDRED
    

    For numerator and denominator of percentage, see type.FractionalPercent

    Key definition 2 - http_filters in service-envoy-fault-injection-delay.yaml

        http_filters:
        - name: envoy.fault
        config:
            delay:
            type: fixed
            fixed_delay: 10s
            percentage:
                numerator: 50
                denominator: HUNDRED
    

    For fixed_delay and percentage of delay injection, see config.filter.fault.v2.FaultDelay

    To continue, go to github

    Circuit Breaker

    Circuit Breaking lets you configure failure thresholds that ensure safe maximums after which these requests stop. This allows for a more graceful failure, and time to respond to potential issues before they become larger.

    • Circuit Breaker is configured in service_red such that Circuit Breaker will be triggered with the following conditions:
      • Envoy makes more than max connection#:1
      • Envoy makes more than max parallel requests#:1

    Key definition - clusters in service-envoy-circuitbreaker.yaml

      clusters:
      - name: local_service
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        circuit_breakers:
          thresholds:
            max_connections: 1
            max_pending_requests: 1
            max_requests: 1
        hosts:
        - socket_address:
            address: 127.0.0.1
            port_value: 8080
    

    For the detail of circuit_breakers configuration, see cluster.CircuitBreakers

    To continue, go to github

    Timeouts and Retries

    Front-proxy configurations are the same as the ones in HTTP Routing: Simple Match Routing. Differences from HTTP Routing: Simple Match Routing are the following 2 additional behaviors:

    • Timeouts (5 seconds) for the request to service_blue
    • Retries that Envoy will attempt to do if service_red responds with any 5xx response code

    For Service Containers, delay fault injection and abort fault injection are configured in service_blue and service_red respectively (which are the same configuration as the ones in Fault Injection Demo)

    Key definition - virtual_hosts in front-envoy.yaml

        virtual_hosts:
        - name: backend
            domains:
            - "*"
            routes:
            - match:
                prefix: "/service/blue"
            route:
                cluster: service_blue
                timeout: 5s
            - match:
                prefix: "/service/green"
            route:
                cluster: service_green
            - match:
                prefix: "/service/red"
            route:
                cluster: service_red
                retry_policy:
                retry_on: "5xx"
                num_retries: 3
                per_try_timeout: 5s
    
    • timeout: (Duration) Specifies the timeout for the route. If not specified, the default is 15s. For more detail, see timeout section in RouteAction
    • retry_policy indicates the retry policy for all routes in this virtual host. For more detail on retry_policy, see route.RetryPolicy

    To continue, go to github

  • Quick Start with Azure Functions V2 Python (Preview)

    Today (Sept 25, 2018 JST), Azure Functions supports Python development using Python 3.6 on the Functions v2 (cross-platform) runtime. You can now use your Python code and dependencies on Linux-based Functions. This is an article on quick start with Azure Functions V2 Python (Preview) showing how you can quickly start Python function development on Azure Function V2 runtime.

    1. Prerequisites for Buidling & Testing Locally

    Prerequisite 1 - Python version

    This is how you switch your python version using venv.

    First of all, install Python 3.6 if you don’t have on your local environemnt.

    $ python3.6 -V
    Python 3.6.1
    

    Then, to build and test Python function with Azure Functions Core Tools (you’ll install after this), you need to setup a Python 3.6 virtual environemnt using venv (NOT, pyenv or some other virtual env tools).

    $ python -V
    Python 2.7.1
    
    # create and activate virutal environemnt named "venv3.6"
    $ python3.6 -m venv venv3.6
    $ source venv3.6/bin/activate
    
    $ Python -V
    Python 3.6.1
    

    [NOTE] As virtualenv (venv) was included in Python3.3, you don’t need to install venv separately. It’s already in your Python 3.6

    Prerequisite 2 - Azure Functions Core Tools

    You need to install azure-functions-core-tools version 2.0.3 or later for buiding & testing Python functions on Azure Functions V2 runtime.

    # Check current package version (only if you already have azure-functions-core-tools installed)
    $ func --version
    2.0.1-beta.35
    
    $ npm list -g  --depth=0 |grep azure-functions-core-tools
    ├── azure-functions-core-tools@2.0.1-beta.35
    
    # Check available package versions
    $ npm info azure-functions-core-tools versions
      '2.0.1-beta.31',
      '2.0.1-beta.32',
      '2.0.1-beta.33',
      '2.0.1-beta.34',
      '2.0.1-beta.35',
      '2.0.1-beta.37',
      '2.0.1-beta.38',
      '2.0.1-beta.39',
      '2.0.1-beta.23-1',
      '2.0.1-beta1.15',
      '2.0.2',
      '2.0.3' ]
    
    # Install speicific package version - 2.0.3
    $ npm install -g azure-functions-core-tools@2.0.3
    
    $ func --version
    2.0.3
    

    2. Create Python Function and run locally

    2-1. Create Python Functions Project

    Create a local Python Functions project - v2projects

    $ func init v2projects --worker-runtime python
    
    Installing wheel package
    Installing azure-functions==1.0.0a4 package
    Installing azure-functions-worker==1.0.0a4 package
    Running pip freeze
    Writing .gitignore
    Writing host.json
    Writing local.settings.json
    Writing /Users/yoichika/dev/github/azure-functions-python-samples/v2projects/.vscode/extensions.json
    

    A new folder named v2projects is created. Then, change directory to this folder

    $ cd v2projects
    

    2-2. Create a python function

    Check function template list

    $ func templates list
    
    C# Templates:
      Blob trigger
      Cosmos DB trigger
      Durable Functions activity
      Durable Functions HTTP starter
      Durable Functions orchestrator
      Event Grid trigger
      Event Hub trigger
      HTTP trigger
      Outlook message webhook creator
      Outlook message webhook deleter
      Outlook message webhook handler
      Outlook message webhook refresher
      Microsoft Graph profile photo API
      Queue trigger
      SendGrid
      Service Bus Queue trigger
      Service Bus Topic trigger
      Timer trigger
    
    JavaScript Templates:
      Blob trigger
      Cosmos DB trigger
      Event Grid trigger
      HTTP trigger
      Queue trigger
      SendGrid
      Service Bus Queue trigger
      Service Bus Topic trigger
      Timer trigger
    
    Python Templates:
      Blob trigger
      Cosmos DB trigger
      Event Grid trigger
      Event Hub trigger
      HTTP trigger
      Queue trigger
      Service Bus Queue trigger
      Service Bus Topic trigger
      Timer trigger
    
    

    Create a python funciton with “HTTP trigger” template

    $ func new --language Python --template "HTTP trigger" --name HttpTriggerPython
    
    Select a template: HTTP trigger
    Function name: [HttpTriggerPython] Writing /Users/yoichika/dev/github/azure-functions-python-samples/v2projects/HttpTriggerPython/sample.dat
    Writing /Users/yoichika/dev/github/azure-functions-python-samples/v2projects/HttpTriggerPython/__init__.py
    Writing /Users/yoichika/dev/github/azure-functions-python-samples/v2projects/HttpTriggerPython/function.json
    The function "HttpTriggerPython" was created successfully from the "HTTP trigger" template.
    

    2-3. Local Testing

    Run the Functions host locally.

    $ func host start
    

    Then, send a test requst to the given endpoint for the HttpTrigger function

    $ curl http://localhost:7071/api/HttpTriggerPython?name=AzureFunctionV2
    
    Hello AzureFunctionV2!
    

    3. Create Linux FunctionApps (Preview Consumption App)

    Now you create Functions App to deploy and run your Python functions on Azure. You actually need Linux V2 Function Apps as publishing Python functions is only supported for Linux Function Apps. So let’s create the Azure Functions Linux Consumption App (which is in preview as of today Sept 25, 2018 JST).

    3-1. Whitelist request for accessing the Azure Functions Linux Consumption (preview)

    As guided in Azure Functions on Linux Preview, you need to email linuxazurefunctions at Microsoft dot com with your Azure subscription ID to get your ID whitelisted to access the Azure Functions Linux Consumption (Preview). [NOTE] This is only needed during its preview preriod.

    3-2. Install the Azure CLI extension for the Azure Functions Linux Consumption preview

    As a prerequisite, you need to install the Azure CLI extension for the Azure Functions Linux Consumption preview. Here is how you do:

    $ curl "https://functionscdn.azureedge.net/public/docs/functionapp-0.0.1-py2.py3-none-any.whl" \
     -o functionapp-0.0.1-py2.py3-none-any.whl
    $ az extension add --source functionapp-0.0.1-py2.py3-none-any.whl
    

    For more detail, see this doc

    3-3. Create a resource group

    RESOURCE_GROUP="RG-azfuncv2"
    REGION="eastus"
    az group create --name $RESOURCE_GROUP --location $REGION
    

    3-4. Create an Azure Storage Account

    STORAGE_ACCOUNT="azfuncv2linuxstore"
    az storage account create --name $STORAGE_ACCOUNT \
        --location $REGION \
        --resource-group $RESOURCE_GROUP \
        --sku Standard_LRS
    

    3-5. Create a Empty Function App on Linux (Consumption Plan)

    Give your function app name to APP_NAME variable, and execute az command like this.

    APP_NAME="yoichikaazfuncv2linux001"
    az functionapp createpreviewapp --name $APP_NAME \
        --resource-group $RESOURCE_GROUP \
        --storage-account $STORAGE_ACCOUNT \
        -l $REGION \
        --runtime python \
        --is-linux
    
    (output)
    Your Linux, cosumption plan, function app 'yoichikaazfuncv2linux001' has been successfully created but is not active until content is published usingAzure Portal or the Functions Core Tools.
    

    [NOTE] The Function App name needs to be unique across all apps in Azure.

    Now you’re ready to publish your functions to the App!

    4. Publish the Python Function

    You publish the Python functions to the function app ( named yoichikaazfuncv2linux001 here )

    # func azure functionapp publish <app_name>
    $ func azure functionapp publish yoichikaazfuncv2linux001 --force
    
    Getting site publishing info...
    pip download -r /Users/yoichika/dev/github/azure-functions-python-samples/v2projects/requirements.txt --dest /var/folders/mn/xw3hp_854lvb5yr2m_00s7sm0000gn/T/azureworkerry44_fwz
    pip download --no-deps --only-binary :all: --platform manylinux1_x86_64 --python-version 36 --implementation cp --abi cp36m --dest /var/folders/mn/xw3hp_854lvb5yr2m_00s7sm0000gn/T/azureworkerpcdk1t4g azure_functions_worker==1.0.0a4
    pip download --no-deps --only-binary :all: --platform manylinux1_x86_64 --python-version 36 --implementation cp --abi cp36m --dest /var/folders/mn/xw3hp_854lvb5yr2m_00s7sm0000gn/T/azureworkerpcdk1t4g azure_functions==1.0.0a4
    pip download --no-deps --only-binary :all: --platform manylinux1_x86_64 --python-version 36 --implementation cp --abi cp36m --dest /var/folders/mn/xw3hp_854lvb5yr2m_00s7sm0000gn/T/azureworkerpcdk1t4g protobuf==3.6.1
    pip download --no-deps --only-binary :all: --platform manylinux1_x86_64 --python-version 36 --implementation cp --abi cp36m --dest /var/folders/mn/xw3hp_854lvb5yr2m_00s7sm0000gn/T/azureworkerpcdk1t4g grpcio_tools==1.14.2
    pip download --no-deps --only-binary :all: --platform manylinux1_x86_64 --python-version 36 --implementation cp --abi cp36m --dest /var/folders/mn/xw3hp_854lvb5yr2m_00s7sm0000gn/T/azureworkerpcdk1t4g setuptools==40.4.3
    pip download --no-deps --only-binary :all: --platform manylinux1_x86_64 --python-version 36 --implementation cp --abi cp36m --dest /var/folders/mn/xw3hp_854lvb5yr2m_00s7sm0000gn/T/azureworkerpcdk1t4g grpcio==1.14.2
    pip download --no-deps --only-binary :all: --platform manylinux1_x86_64 --python-version 36 --implementation cp --abi cp36m --dest /var/folders/mn/xw3hp_854lvb5yr2m_00s7sm0000gn/T/azureworkerpcdk1t4g six==1.11.0
    
    Preparing archive...
    Uploading content...
    Upload completed successfully.
    Deployment completed successfully.
    Removing 'WEBSITE_CONTENTSHARE' from 'yoichikaazfuncv2linux001'
    Removing 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' from 'yoichikaazfuncv2linux001'
    Syncing triggers...
    

    [NOTE]

    1. if you run func azure functionapp publish without --force opiton, you’ll come up with the following the message:
       Your app is configured with Azure Files for editing from Azure Portal.
       To force publish use --force. This will remove Azure Files from your app.
      
    2. Please be noted that when deployging from func command( ie, running from a package), the file system in Azure Function App is read-only and no changes can be made to the files. To make any changes update the content in your zip file and WEBSITE_RUN_FROM_PACKAGE app setting.

    Looks like pulishing has completed successfully, so let’s access to the function on Azure

    $ curl https://yoichikaazfuncv2linux001.azurewebsites.net/api/HttpTriggerPython?name=AzureFunctionV2
    
    Hello AzureFunctionV2!
    

    Enjoy Azure Functions V2 Python!

  • Accessing RBAC enabled Kubernetes Dashboard

    This is an article on how you can configure Service Account and RoleBinding in order to make Dashbaord work. As of release Kubernetes v1.7, Dashboard no longer has full admin privileges granted by default. All the privileges are revoked and only minimal privileges granted, that are required to make Dashboard work. With default priviledge, you’ll see the following errors showed up on the Dashboard.

    [Azure Kubernetes Service (AKS)] RBAC is enabled by default

    Since Azure CLI version 2.0.40, RBAC is enabled by default. As you can see in the Azure command help, your cluster is RBAC enabled unless you specify --disable-rbac during the creation of the cluster.

    $ az --version
      azure-cli (2.0.43)
    
    $ az aks create --help
    
        --disable-rbac                 : Disable Kubernetes Role-Based Access Control.
        --enable-rbac -r  [Deprecated] : Enable Kubernetes Role-Based Access Control. Default:
                                         enabled.
            Argument 'enable_rbac' has been deprecated and will be removed in a future release. Use
            '--disable-rbac' instead.
    

    Option 1: Access to Dashboard with your Service Account

    In option 1, I introduce how to give priviledge your Service Account and access to the Dashboard with the account. Actually there are a couple of authorization options, and here I introduce how to authorize with Bear Token

    [NOTE] According to this,

    As of release 1.7 Dashboard supports user authentication based on:

    • Authorization: Bearer header passed in every request to Dashboard. Supported from release 1.6. Has the highest priority. If present, login view will not be shown.
    • Bearer Token that can be used on Dashboard login view.
    • Username/password that can be used on Dashboard login view (Disabled by default)
    • Kubeconfig file that can be used on Dashboard login view

    1-1. Create your Service Account for Dashboard access

    First of all, create your Service Account my-admin-user like this:

    $ kubectl create serviceaccount my-admin-user -n kube-system
    

    Or you can create the Service Account with the following YAML my-sa.yaml and deploying it with kubectl create -f my-sa.yaml:

    # my-sa.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: my-admin-user
      namespace: kube-system
    

    Check if your Service Account (my-admin-user) has been added.

    $ kubectl get sa -n kube-system
    
    NAME                                                          SECRETS   AGE
    addon-http-application-routing-external-dns                   1         9d
    addon-http-application-routing-nginx-ingress-serviceaccount   1         9d
    attachdetach-controller                                       1         9d
    certificate-controller                                        1         9d
    clusterrole-aggregation-controller                            1         9d
    cronjob-controller                                            1         9d
    daemon-set-controller                                         1         9d
    default                                                       1         9d
    deployment-controller                                         1         9d
    disruption-controller                                         1         9d
    endpoint-controller                                           1         9d
    generic-garbage-collector                                     1         9d
    heapster                                                      1         9d
    horizontal-pod-autoscaler                                     1         9d
    job-controller                                                1         9d
    kube-dns                                                      1         9d
    kube-proxy                                                    1         9d
    kube-svc-redirector                                           1         9d
    kubernetes-dashboard                                          1         9d
    my-admin-user                                                 1         15s
    namespace-controller                                          1         9d
    node-controller                                               1         9d
    persistent-volume-binder                                      1         9d
    pod-garbage-collector                                         1         9d
    pv-protection-controller                                      1         9d
    pvc-protection-controller                                     1         9d
    replicaset-controller                                         1         9d
    replication-controller                                        1         9d
    resourcequota-controller                                      1         9d
    route-controller                                              1         9d
    service-account-controller                                    1         9d
    service-controller                                            1         9d
    statefulset-controller                                        1         9d
    ttl-controller                                                1         9d
    tunnelfront                                                   1         9d
    

    1-2. Binding the role cluster-admin to the Service Account

    Create a ClusterRoleBinding which gives the role cluster-admin (= full admin priviledge) to the ServiceAccount my-admin-user

    $ kubectl create clusterrolebinding my-admin-user -n kube-system --clusterrole=cluster-admin --serviceaccount=kube-system:my-admin-user
    

    Or you can grant the priviledges with the following YAML my-sa-binding.yaml and deploying it with kubectl create -f my-sa-binding.yaml:

    # my-sa-binding.yaml
    apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: ClusterRoleBinding
    metadata:
      name: my-admin-user
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: cluster-admin
    subjects:
    - kind: ServiceAccount
      name: my-admin-user
      namespace: kube-system
    

    1-3. Authentication Option - Give Bear Token at Dashboard login view

    1-3-1. Get the Token of the ServiceAccount

    $ kubectl get secret $(kubectl get serviceaccount my-admin-user -n kube-system -o jsonpath="{.secrets[0].name}") -o jsonpath="{.data.token}" -n kube-system | base64 --decode
    

    Or you can obtain the token step by step like this:

    # Get secret name for my-admin-user
    $ kubectl get serviceaccount my-admin-user -n kube-system -o yaml
    
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      creationTimestamp: 2018-08-11T06:53:34Z
      name: my-admin-user
      namespace: kube-system
      resourceVersion: "1075968"
      selfLink: /api/v1/namespaces/kube-system/serviceaccounts/my-admin-user
      uid: 44142169-9d33-11e8-b7d0-de454880a5dc
    secrets:
    - name: my-admin-user-token-nzp4f
    
    
    # Get secret string and base64 decoded it
    $ kubectl get secret my-admin-user-token-nzp4f -n kube-system -o jsonpath="{.data.token}" | base64 --decode
    
    eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJteS1hZG1pbi11c2VyLXRva2VuLW56cDRmIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6Im15LWFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI0NDE0MjE2OS05ZDMzLTExZTgtYjdkMC1kZTQ1NDg4MGE1ZGMiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06bXktYWRtaW4tdXNlciJ9.2ulwCWd7MOnQjoecAY2NoleIxcHD8tda97ud3cK-kHWmdCodAejvddA4YjYozzBu2bWNA83aVTvKAn5-Uv1DC47U5FPh2LXAXNPXn4PyrdLO7TFZdHYmkvUgJKsg25vJvJmsWF9eQOinjjh_g16aGgdxrWz0NGJz5d1eE5GDP5NXXTTgxXlD_GFQduhlq8kc89dhpDUXMYe60-KzZvNaQhIskPsnxHMix1JrHEdtfciFhHRb2CBNjPWfcg455NGoS9S-k0qTfoIHYJC627p75E8TGqyTIa8TSg8vaif4XWgeg_OZWqEIGHTIrhEAGO4ElFijdZuzAg2-v9BGWe8i4q1i70ca5CwReJTG8t13eeOoEkq--VbhDAMY6rxmx-hi9dwf-zjsD233MdHJLh1yRi0eo_k5ov7fwDDsLQXeCTBIjSAzorvXseWr5m9sQ7yREbjDXCOsHbYo5xNV5ii-yOlxYyiqPxZZnnSwzllj1lwPDLSL0MyxkR9siF52vbkNDe6qdYYMqPtA-jTMIw_iLlB-WeN1Fx8423c4x5wV6IGPJZFuOYZhB0ra4jfRSS39vesaNodW8RjHUiuOSVA8_j-DxwOxa8prynALFWGswSMy6PfVQydouU6vammeqPBel9-IqBeTXY-57YumELG1PdcOcxdrBCZUlxBvJWbItxA
    

    1-3-2. Give Bear Token at Dashboard login view

    Now that you have a bear token that you need to grant the full priviledges to your Service Account, let’s access to the Dashboard login view.

    First of all, create a proxy to the dashboard:

    $ kubectl proxy
    
    Starting to serve on 127.0.0.1:8001
    

    Then, access to the Dashboard login page:

    $ open http://localhost:8001/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy/#!/login
    

    Or if it’s on Azure, you can leverage Azure CLI command to access the Dashboard like this:

    $ az aks browse --resource-group <RESOURCE_GROUP> --name <CLUSTER_NAME>
    $ open http://127.0.0.1:8001/#!/login
    

    You will see the following Dashboard login view. Choose Token option and enter the bear token you got above. You will be able to access and operate with the Dashboard without any errors.

    Easy login by giving Authorization header using Browser extention

    As introduced in here, install Requestly browser plugin and configure to make Dashboard use authorization header. You simply need to configure the plugin to pass the following header in accessing the dashboard:

    Authorization: Bearer <token>
    

    HERE is the example screen shot:

    Option2: Granting admin privileges to Dashboard’s Service Account

    In Option 2, I introduce how to give full privilege (role: cluster-admin) to the Dashboard’s Service Account kubernetes-dashboard. With this option, you can skip the authorization process that you do in the option 1 to access Dashboard. However, as mentioned here, granting admin privileges to Dashboard’s Service Account might be a security risk.

    First of all, create a ClusterRoleBinding which gives the role cluster-admin (= full admin priviledge) to the ServiceAccount kubernetes-dashboard

    $ kubectl create clusterrolebinding kubernetes-dashboard -n kube-system --clusterrole=cluster-admin --serviceaccount=kube-system:kubernetes-dashboard
    

    Or you can do the same with the following YAML dashboard-sa-binding.yaml and deploying it with kubectl create -f dashboard-sa-binding.yaml:

    # dashboard-sa-binding.yaml
    apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: ClusterRoleBinding
    metadata:
      name: kubernetes-dashboard
      labels:
        k8s-app: kubernetes-dashboard
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: cluster-admin
    subjects:
    - kind: ServiceAccount
      name: kubernetes-dashboard
      namespace: kube-system
    

    Finally, access to the Dashboard. You’ll be able to access and operate without any errors.

  • Moving to Jekyll based Github page

    I’ve moved my blog site from Wordpress to Jekyll based Github page. There are a few reasons for this:

    • I wanted to manage my blog data on Github
    • I wanted to switch from HTML based to Markdown
    • I wanted more static approach like generating once, not dynamically rendering for every request (for performance reason)

    After a few minutes of googling, I came up with Jekyll and found it much easier to manage my blog data with Jekyll than with Wordpress. I love Jekyll’s simplicity. For the one who want to do the same migration, I’d like to share memo on how I migrated the blog site from Wordpress to Jekyll based Github page.

    What to do (What I’ve done)

    • [ENV] Wordpress is running on Ubuntu 16.04, and I work on all migration works on MacOS (10.13.5)
    • Importing my posts from a self-hosted Wordpress
      • Install jekyll-import ruby gem and all additional packages needed to import my posts from a self-hosted Wordpress
      • Import my posts from the Wordpress
      • Convert HTML files to Markdown format files (including manual works)
    • Publish the site uisng Jekyll and Github Pages
      • Check ruby and github-pages version used in Github page, and install the same github-pages gem on the same ruby version
      • Run Jekyll locally and verify the site content
      • Create Github project for hosting the site
      • Publish the site to Github uisng git
      • Setup Custom domain
    • Tips

    Importing my posts from a self-hosted Wordpress

    Install ruby gem packages to import posts from Wordpress

    First, check ruby version as jekyll require > ruby-2.X, and upgrade its version if it’s not > ruby-2.X (I use rbenv to manage multiple ruby versions on MacOS)

    $ ruby --version
    ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin17]
    

    Install jekyll-import ruby gem and all additional packages needed to import my posts from a self-hosted Wordpress

    $ gem install jekyll-import
    # Additional packages for importing from Wordpress
    $ gem install sequel
    $ gem install unidecode
    $ gem install htmlentities
    $ gem install mysql2
    

    Import my posts from the Wordpress

    Run the following command to import my posts from the hosted Wordpress. Make sure that MySQL server used in Wordpress is accessible

    ruby -rubygems -e 'require "jekyll-import";
        JekyllImport::Importers::WordPress.run({
          "dbname"   => "<db name>",
          "user"     => "<db user>",
          "password" => "<db password>",
          "host"     => "<db host or IP>",
          "port"     => "<db port: 3306>",
          "socket"   => "",
          "table_prefix"   => "wp_",
          "site_prefix"    => "",
          "clean_entities" => true,
          "comments"       => false,
          "categories"     => true,
          "tags"           => true,
          "more_excerpt"   => true,
          "more_anchor"    => true,
          "status"         => ["publish"]
        })'
    

    All imported blog ports (in HTML) are stored in _posts directory. See also Jekyll documentation

    Convert HTML files to Markdown format files (including manual works)

    Frist, I converted all HTML files stored in _posts directory to markdown using h2m npm package

    # Install h2m npm package
    $ npm install h2m -g
    # Here is how to conver html to markdown using h2m
    # h2m test.html > test.md
    
    # Run the following one liner converted all HTML files to markdown
    for h in `ls -1 *.html`;do echo $h; m=$(echo $h | sed s/\.html//g).md; h2m $h > $m;  ;done
    

    Then, do some manual modification to *.md files. Markdown don’t support all HTML components such as embeded, iframe, align, etc. What I did for this modification parts are basically:

    • All embeded parts such as Youtube video, slideshare, and gist links (Markdown doesn’t support embeded tags)
    • Table tags part (I used Wordpress Table plugin that express table with very special tag, which of course can not be automatically convered to Markdown )

    Markdown CheatSheet was very helpful in this part

    Publish the site uisng Jekyll and Github Pages

    Install the same github-pages gem and ruby as the ones used in Github page

    Go to this page to get ruby and github-pages version used in Github page. This time it was

    • ruby: 2.4.2
    • github-pages: 186

    Then, I installed the exactly the same version of ruby and github-pages gem on my local

    # Install Ruby using rbenv
    $ rbenv install 2.4.2
    $ rbenv global 2.4.2
    
    # Install github-pages ruby gem package
    $ gem install github-pages -v 186
    

    Run Jekyll locally and verify the site content

    Create Jekyll template site and copy all blog pages (Markdown) on to _posts directory under the site template root

    # Check jekyll version
    $ jekyll -v
    jekyll 3.7.3
    
    # Create new Jekyll template site with jekyll command (here I create template site named `myblog`)
    $ jekyll new myblog
    $ tree myblog
    myblog
    ├── 404.html
    ├── Gemfile
    ├── _config.yml
    ├── _posts
    │   └── 2018-07-06-welcome-to-jekyll.markdown
    ├── about.md
    └── index.md
    
    # Remove Gemfile
    # Ruby will use the contents of your Gemfile to build your Jekyll site. So you basically add the same version of github-pages and ruby to Gemfile as the ones in Github Pages, but I removed it to make life simple as I've already synced the local version of github-pages and ruby with the ones in Github Pages but. If you possibly run Jekyll site on multiple environments, it's definitly better to manage package versions with Gemfile
    $ rm Gemfile
    
    # Copy all blog pages (Markdown) on to `_posts` directory under the site template root
    $ cp *.md _posts/
    

    Run the local webserver like this and verify the site content:

    $ jekyll s
    $ curl http://localhost:4000
    

    See also Setting up your GitHub Pages site locally with Jekyll

    Create Github project for hosting the site

    Create my user site by following instructions in https://pages.github.com/. In this case, I’ve created yokawasa.github.io under my account https://github.com/yokawasa, by which I can access *.html|*.md in github.com/yokawasa/yokawasa.github.io with the URL of https://yokawasa.github.io/(*.html|*.md)

    Publish the site to Github uisng git

    Copy all files under Jekyll site content that I’ve verified onto yokawasa.github.io and push them to Github

    $ git clone https://github.com/yokawasa/yokawasa.github.io
    $ cp -pr myblog/* yokawasa.github.io/
    # make sure to copy .gitignore file as well
    
    $ cd yokawasa.github.io
    $ git add -all
    $ git commit -m "Initial commit"
    $ git push -u origin master
    $ open https://yokawasa.github.io
    

    Setup Custom domain

    I added my custom domain (unofficialism.info) to my Github page site by following Quick start: Setting up a custom domain. Here are what I’ve actually done:

    First, check my current DNS record for my custom domain

    $ dig +noall +answer unofficialism.info
    unofficialism.info.     146     IN      A       XX.XXX.XXX.XX
    

    Then, follow my DNS provider’s instructions to create A records that point my custom domain to the following IP addresses (These are fixed IPs but these IPs can be obtained simply by running dig for username.github.com):

    185.199.108.153
    185.199.109.153
    185.199.110.153
    185.199.111.153
    

    Confirm that the DNS record is set up correctly

    $ dig +noall +answer unofficialism.info
    
    unofficialism.info.     430     IN      A       185.199.108.153
    unofficialism.info.     430     IN      A       185.199.109.153
    unofficialism.info.     430     IN      A       185.199.110.153
    unofficialism.info.     430     IN      A       185.199.111.153
    

    In addition, add my custom domain for my GitHub Pages site in Github Page setting page by following Adding or removing a custom domain for your GitHub Pages site

    Finally, confirm the access to my Github page with my custom domain

    $ curl https://unofficialism.info
    
  • Kubernetes x PaaS コンテナアプリケーションのNoOpsへの挑戦 (Japan Container Days v18.04)

    先日、4月19日に開催されたJapan Container Days v18.04にて「Kubernetes x PaaS – コンテナアプリケーションのNoOpsへの挑戦」というタイトルでセッションを担当させていただいた。その名の通りメインがKubernetesで、KubernetesアプリケーションにおいてNoOps(運用レス)を目指すためのにどういった工夫ができるのか、どういったものを活用していけばよいのか、という内容です。このブログではJapan Container Daysでの発表に使用したスライドの共有とセッションに中のサンプルやデモについて補足させていただく。

    Session Slides

    Kubernetes x PaaS – コンテナアプリケーションの NoOpsへの挑戦 from Yoichi Kawasaki

    補足情報

    1. Open Service Broker for AzureでAzure Database for MySQLの利用

    スライドでお見せした実際のファイルを使ってAzure Database for MySQLのサービスインスタンス作成、バインディング、そして実際のアプリケーションからの利用までの流れを紹介させていただく。

    Open Service Broker for AzureプロジェクトのGithubにあるサンプルファイルmysql-instance.yamlmysql-binding.yamlを使ってそれぞれServiceInstanceとServiceBindingを作成する `

    # Provisioning the database, basic50 plan ...
    $ kubectl create -f mysql-instance.yaml
    
    # Wait until ServiceInstance named example-mysql-instance get ready 'Status => Ready',
    # then execute the following to create a binding for this new database,
    $ kubectl create -f mysql-binding.yaml
    

    これでexample-mysql-secretという名前のシークレットオブジェクトが作成され、そこにアプリケーションがAzure Database for MySQLインスタンスとの接続必要な情報が格納される。ここではMySQLに接続して単純な処理をするだけのPythonアプリでテストする。アプリをデプロイメントのために下記YAML (mysql-deploy.yaml)を用意したが注目すべきはYAMLの中でMySQLの接続必要な情報をシークレットexample-mysql-secretから取得している点。

    apiVersion: apps/v1beta1
    kind: Deployment
    metadata:
      name: sample-osba-mysql
    spec:
      replicas: 1
      template:
        metadata:
          labels:
            app: sample-osba-mysql
        spec:
          containers:
          - name: osba-mysql-demo
            image: yoichikawasaki/sample-osba-mysql:0.0.1
            ports:
            - containerPort: 80
            env:
            - name: MYSQL_USER
              valueFrom:
                secretKeyRef:
                  key: username
                  name: example-mysql-secret
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  key: password
                  name: example-mysql-secret
            - name: MYSQL_HOST
              valueFrom:
                secretKeyRef:
                  key: host
                  name: example-mysql-secret
            - name: MYSQL_DATABASE
              valueFrom:
                secretKeyRef:
                  key: database
                  name: example-mysql-secret
    

    また、アプリケーションの中ではYAMLで指定された環境変数からMySQL接続に必要な情報を取得している。以下、サンプルアプリのコード(sample-osba-mysql.py)。

    import MySQLdb as db
    import os
    
    # MySQL configurations
    MYSQL_USER = os.environ['MYSQL_USER']
    MYSQL_PASSWORD = os.environ['MYSQL_PASSWORD']
    MYSQL_HOST = os.environ['MYSQL_HOST']
    MYSQL_DATABASE = os.environ['MYSQL_DATABASE']
    
    print(MYSQL_USER)
    print(MYSQL_PASSWORD)
    print(MYSQL_HOST)
    print(MYSQL_DATABASE)
    
    def main():
        conn = db.connect(
                user=MYSQL_USER,
                passwd=MYSQL_PASSWORD,
                host=MYSQL_HOST,
                db=MYSQL_DATABASE
            )
        c = conn.cursor()
    
        sql = 'drop table if exists test'
        c.execute(sql)
    
        sql = 'create table test (id int, content varchar(32))'
        c.execute(sql)
    
        sql = 'show tables'
        c.execute(sql)
        print('===== table list =====')
        print(c.fetchone())
    
        # insert records
        sql = 'insert into test values (%s, %s)'
        c.execute(sql, (1, 'hoge'))
    
        datas = [
            (2, 'foo'),
            (3, 'bar')
        ]
        c.executemany(sql, datas)
    
        # select records
        sql = 'select * from test'
        c.execute(sql)
        print('===== Records =====')
        for row in c.fetchall():
            print('Id:', row[0], 'Content:', row[1])
    
        conn.commit()
        c.close()
        conn.close()
    
    
    if __name__ == '__main__':
        main()
    

    それでは次のようにアプリケーションをデプロイする

    $ kubectl create -f mysql-deploy.yaml
    

    全ての過程が問題なければ、デプロイ後のPodのログをみると、下記のような出力が確認できるはずだ。これはアプリケーションがMySQLとの接続して処理が行われたことを表す。ここではsternを使ってログを参照している。

    $ stern <アプリケーションのPod名>
    
    + sample-osba-mysql-684ccd679f-nl252 › osba-mysql-demo
    sample-osba-mysql-684ccd679f-nl252 osba-mysql-demo o36e6ivtni@8ae3fca5-9b5c-471b-99d8-0eeb567c6acb
    sample-osba-mysql-684ccd679f-nl252 osba-mysql-demo sE8UFm5cN4tgIeNF
    sample-osba-mysql-684ccd679f-nl252 osba-mysql-demo 8ae3fca5-9b5c-471b-99d8-0eeb567c6acb.mysql.database.azure.com
    sample-osba-mysql-684ccd679f-nl252 osba-mysql-demo v8i0xxlerz
    sample-osba-mysql-684ccd679f-nl252 osba-mysql-demo ===== table list =====
    sample-osba-mysql-684ccd679f-nl252 osba-mysql-demo ('test',)
    sample-osba-mysql-684ccd679f-nl252 osba-mysql-demo ===== Records =====
    sample-osba-mysql-684ccd679f-nl252 osba-mysql-demo Id: 1 Content: hoge
    sample-osba-mysql-684ccd679f-nl252 osba-mysql-demo Id: 2 Content: foo
    sample-osba-mysql-684ccd679f-nl252 osba-mysql-demo Id: 3 Content: bar
    

    2. デモ - Virtual Kubelet + Azure Container Instances

    3. デモ - Traffic Routing with Istio

    セッション中に時間が足りなくてチラ見せ程度しかお見せできなかったサービスメッシュにIstio(Istio-0.5.0)を使ったトラフィックルーティングデモ。せっかくなのでここで全ての手順を紹介させていただく。アプリな単純なコンテナイメージのタグ名を表示するだけのFlaskアプリを利用している

    必要ファイル一覧

    まずはV1とV2アプリケーションのデプロイメント。ここではアプリケーションのManifestに対してistioctlのkube-injectでIstioの設定が組み込まれたManifestを元にデプロイメントしている。

    ## ver1.0
    $ kubectl apply -f <(istioctl kube-inject --debug -f myversion-v1.yaml)
    ## ver2.0
    $ kubectl apply -f <(istioctl kube-inject --debug -f myversion-v2.yaml)
    

    上記のデプロイメントで作られたIngressコントローラーのIP、PORTを取得を次のように取得する。サンプルで使用しているFlaskアプリで指定しているエンドポイントが/versionであるからアプリケーションのエンドポイントはGATEWAY_IP:GATEWAY_PORT/versionがとなる。

    GATEWAY_IP=$(kubectl get po -n istio-system -l \
            istio=ingress -n istio-system \
            -o 'jsonpath={.items[0].status.hostIP}')
    
    GATEWAY_PORT=$(kubectl get svc istio-ingress \
            -n istio-system -n istio-system \
            -o 'jsonpath={.spec.ports[0].nodePort}')
    
    GATEWAY_URL=$GATEWAY_IP:$GATEWAY_PORT
    echo $GATEWAY_URL
    

    まずは、Istioに全てのトラフィックをv1アプリにルーティングするように設定する

    $ istioctl create -f route-default-v1.yaml
    

    次のように連続でアクセスして全てのトラフィックがV1にルーティングされていることを確認

    $ while true; do curl http://${GATEWAY_URL}/version; sleep 1; done
    
    I am v1.111111111
    I am v1.111111111
    I am v1.111111111
    I am v1.111111111
    

    続いて、Istioにさきほどの100% V1の設定を削除して、新しく90%のトラフィックをv1アプリに 10%をV2アプリにルーティングするように設定する

    $ istioctl delete routerule myversion-default-v1 -n default
    $ istioctl create -f route-canary.yaml
    

    前と同様に連続でアクセスして90%のトラフィックはV1に、10%はV2にルーティングされていることを確認

    $ while true; do curl http://${GATEWAY_URL}/version; sleep 1; done
    
    I am v1.111111111
    I am v1.111111111
    I am v1.111111111
    I am v2.222222222  ... たまにV2
    I am v1.111111111
    

    最後に、Istioに特定の条件でのみ100%のトラフィックをV2アプリ向けるように設定する。下記設定ファイルroute-conditional-v2.yamlに記述されているように、ここではHTTPヘッダのuserの値が”yoichi”であることを特定の条件とする。

    ---
    apiVersion: config.istio.io/v1alpha2
    kind: RouteRule
    metadata:
      name: myversion-conditional-v2
    spec:
      destination:
        name: myversion
      precedence: 2
      match:
        request:
          headers:
            user:
              regex: "yoichi"
      route:
      - labels:
          version: v2.0
    

    Istioに特定条件用のルーティング設定を追加

    $ istioctl create -f route-conditional-v2.yaml
    

    次のようにヘッダーに”user: yoichi”の条件を追加して連続でアクセスして全てのトラフィックがV2にルーティングされていることを確認

    $ while true; do curl -H "user: yoichi" http://${GATEWAY_URL}/version; sleep 1; done
    
    I am v2.222222222
    I am v2.222222222
    I am v2.222222222
    I am v2.222222222
    

    以上、補足情報でした。

  • Controlling Azure Media Services traffic with Traffic Manager

    This is an article on how you can achieve Azure Media Services (AMS) streaming traffic distribution with Traffic Manager.

    The process for a client to find target AMS streaming endpoints

    The figure shows how a client find target AMS streaming endpoints with Traffic Manager and requests from video players are distributed to streaming endpoints in AMS:

    Controling Azure Media Services Traffic with Traffic Manager

    When AMS endpoints are added to an Azure Traffic Manager profile, Azure Traffic Manager keeps track of the status of the endpoints (running, stopped, or deleted) so that it can decide which of those endpoints should receive traffic. You can configure the way to route network traffic to the endpoints by choosing traffic routing methods available in Traffic Manager.

    Configuration procedure

    Suppose that you have 2 AMS accounts (amsaccount1, amsaccount2), and that you want to distribute requests to your Traffic Manager domain (myamsstreaming.trafficmanager.net) from video player clients to streaming endpoints in AMS. When AMS endpoints are added to an Azure Traffic Manager profile (myamsstreaming), Azure Traffic Manager keeps track of the status of your AMS streaming endpoints (running, stopped, or deleted) so that it can decide which of those endpoints should receive traffic. However, when it comes to AMS endpoints, it has to be toward a custom domain, NOT simply a Traffic Manager domain for clients’ traffic to be distributed into AMS endpoints using Traffic Manager. So let’s suppose you prepare a custom domain: streaming.mydomain.com.

    Example accounts and domains

    • AMS Account1: amsaccount1 (Streaming endpoint: amsaccount1.streaming.mediaservices.windows.net)
    • AMS Account2: amsaccount2 (Streaming endpoint: amsaccount2.streaming.mediaservices.windows.net)
    • Traffic Manager Domain: myamsstreaming.trafficmanager.net
    • Custom Domain: streaming.mydomain.com

    (1) Point a custom domain to a Traffic Manager domain

    You add the following alias (CNAME) to point the custom domain to the traffic manager domain:

    streaming.mydomain.com IN CNAME myamsstreaming.trafficmanager.net
    

    Useful Link for this step: Point a company Internet domain to an Azure Traffic Manager domain

    (2) Add Azure Media Services origin hosts to the Traffic Manager

    Add AMS endpoints to an Azure Traffic Manager profile as “External Endpoint” via either Azure Portal or Azure CLI / PowerShell. Here is an image of adding endpoints in Azure portal:

    Add endpoints to Traffic Manager

    Useful Link for this step: Add, disable, enable, or delete endpoints

    (3) Custom domain name ownership verification

    First of all, get Media Service Account IDs (GUID) for your AMS accounts in this step. To find the Azure Media Service ID , go to the Azure portal and select your Media Service account. The Azure Media Service ID appears on the right of the DASHBOARD page. Let’s suppose you get the following account IDs for AMS accounts (amsaccount1, amsaccount2):

    • Account ID: 8dcbe520-59c7-4591-8d98-1e765b7f3729 for AMS account: amsaccount1
    • Account ID: 5e0e6784-4ed0-40a0-8444-33da6d4f7171 for AMS account: amsaccount2

    Then, create CNAME that maps (accountId).(parent custom domain) to verifydns.(mediaservices-dns-zone). This is necessary to proves that the Azure Media Services ID has the ownership of the custom domain. Here are CNAMEs to create in this step:

    ## <MediaServicesAccountID>.<custom parent domain> IN CNAME verifydns.<mediaservices-dns-zone>
    
    ## Custom name Ownership verification for amsaccount1
    8dcbe520-59c7-4591-8d98-1e765b7f3729.mydomain.com IN CNAME  verifydns.mediaservices.windows.net
    
    ## Custom name Ownership verification for amsaccount2
    5e0e6784-4ed0-40a0-8444-33da6d4f7171.mydomain.com IN CNAME  verifydns.mediaservices.windows.net
    

    Refer to CustomHostNames section of StreamingEndpoint document to learn more about the configuration in this step.

    (4) Add Custom host name to each Azure Media Service streaming endpoint

    Once you completed “Custom domain name ownership verification” configuration, you then need to configure to add custom host name to each Azure Media Service streaming endpoint. Unfortunately new portal doesn’t include capability to add custom domain to the streaming endpoint. You can set it either using REST API directly or using Azure Media Explorer. Here is how you add custom host name to the streaming endpoint in Azure Media Explorer:

    azure-media-explorer-custom-host-streaming-endpoint

    Choose “Streaming endpoint” in the top menu, right click on your streaming endpoint, and select “Streaming endpoint information and settings”. In Streaming endpoint information form, type in your custom host name for the endpoint, and click “Update settings and close”. That’s it.

    (5) Test video playback with your custom domain

    Once all configurations above are completed (+ DNS settings are reflected), you will see the custom domain name lookup points to either of AMS endpoints added to the traffic manager like this:

    $ dig streaming.mydomain.com
    
    ;; ANSWER SECTION:
    streaming.mydomain.com. 600 IN CNAME myamsstreaming.trafficmanager.net.
    myamsstreaming.trafficmanager.net. 300 IN CNAME amsaccount1.streaming.mediaservices.windows.net.
    amsaccount1.streaming.mediaservices.windows.net. 60 IN CNAME wamsorigin-origin-903f1f37105244fba2270ae7b64021bd.cloudapp.net.
    wamsorigin-origin-903f1f37105244fba2270ae7b64021bd.cloudapp.net. 60 IN A 104.215.4.76
    

    Make sure to check if the custom name lookup points to the other endpoint when one of the endpoints are down.

    Finally, check if you can playback video with your custom domain.

    $ curl http://amsaccount1.streaming.mediaservices.windows.net/ee2e5286-d3fa-40fc-a393-c3c2a3ca5a84/BigBuckBunny.ism/manifest
    
     <?xml version="1.0" encoding="UTF-8"?><SmoothStreamingMedia MajorVersion="2" MinorVersion="2" Duration="84693333" TimeScale="10000000"><StreamIndex Chunks="2" Type="audio" Url="QualityLevels({bitrate})/Fragments(aac_eng_2_128={start time})" QualityLevels="1" Language="eng" Name="aac_eng_2_128"><QualityLevel AudioTag="255" Index="0" BitsPerSample="16" Bitrate="128000" FourCC="AACL" CodecPrivateData="1190" Channels="2" PacketSize="4" SamplingRate="48000" /><c t="0" d="60160000" /><c d="24533333" /></StreamIndex><StreamIndex Chunks="2" Type="video" Url="QualityLevels({bitrate})/Fragments(video={start time})" QualityLevels="5"><QualityLevel Index="0" Bitrate="2896000" FourCC="H264" MaxWidth="1280" MaxHeight="720" CodecPrivateData="000000016764001FACD9405005BB011000000300100000030300F18319600000000168EBECB22C" /><QualityLevel Index="1" Bitrate="1789000" FourCC="H264" MaxWidth="960" MaxHeight="540" CodecPrivateData="000000016764001FACD940F0117EF011000003000100000300300F1831960000000168EBECB22C" /><QualityLevel Index="2" Bitrate="946000" FourCC="H264" MaxWidth="640" MaxHeight="360" CodecPrivateData="000000016764001EACD940A02FF97011000003000100000300300F162D960000000168EBECB22C" /><QualityLevel Index="3" Bitrate="612000" FourCC="H264" MaxWidth="480" MaxHeight="270" CodecPrivateData="0000000167640015ACD941E08FEB011000000300100000030300F162D9600000000168EBECB22C" /><QualityLevel Index="4" Bitrate="324000" FourCC="H264" MaxWidth="320" MaxHeight="180" CodecPrivateData="000000016764000DACD941419F9F011000000300100000030300F14299600000000168EBECB22C" /><c t="0" d="60000000" /><c d="24583333" /></StreamIndex></SmoothStreamingMedia>
    
    $ curl http://streaming.mydomain.com/ee2e5286-d3fa-40fc-a393-c3c2a3ca5a84/BigBuckBunny.ism/manifest
    
     <?xml version="1.0" encoding="UTF-8"?><SmoothStreamingMedia MajorVersion="2" MinorVersion="2" Duration="84693333" TimeScale="10000000"><StreamIndex Chunks="2" Type="audio" Url="QualityLevels({bitrate})/Fragments(aac_eng_2_128={start time})" QualityLevels="1" Language="eng" Name="aac_eng_2_128"><QualityLevel AudioTag="255" Index="0" BitsPerSample="16" Bitrate="128000" FourCC="AACL" CodecPrivateData="1190" Channels="2" PacketSize="4" SamplingRate="48000" /><c t="0" d="60160000" /><c d="24533333" /></StreamIndex><StreamIndex Chunks="2" Type="video" Url="QualityLevels({bitrate})/Fragments(video={start time})" QualityLevels="5"><QualityLevel Index="0" Bitrate="2896000" FourCC="H264" MaxWidth="1280" MaxHeight="720" CodecPrivateData="000000016764001FACD9405005BB011000000300100000030300F18319600000000168EBECB22C" /><QualityLevel Index="1" Bitrate="1789000" FourCC="H264" MaxWidth="960" MaxHeight="540" CodecPrivateData="000000016764001FACD940F0117EF011000003000100000300300F1831960000000168EBECB22C" /><QualityLevel Index="2" Bitrate="946000" FourCC="H264" MaxWidth="640" MaxHeight="360" CodecPrivateData="000000016764001EACD940A02FF97011000003000100000300300F162D960000000168EBECB22C" /><QualityLevel Index="3" Bitrate="612000" FourCC="H264" MaxWidth="480" MaxHeight="270" CodecPrivateData="0000000167640015ACD941E08FEB011000000300100000030300F162D9600000000168EBECB22C" /><QualityLevel Index="4" Bitrate="324000" FourCC="H264" MaxWidth="320" MaxHeight="180" CodecPrivateData="000000016764000DACD941419F9F011000000300100000030300F14299600000000168EBECB22C" /><c t="0" d="60000000" /><c d="24583333" /></StreamIndex></SmoothStreamingMedia>
    
  • 15分でお届けするElastic Stack on Azure設計・構築ノウハウ

    UPDATED on Feb 3, 2018 - Elastic社イベントサイトを追加

    イベント開催日から少々時間が経過したが、Elastic {ON} Tour 2017 東京(2017年12月14日開催)というElastic社オフィシャルのユーザーカンファレンスにて登壇させていただく機会があり、そこで「15分でお届けする Elastic Stack on Azure 設計・構築ノウハウ」というお題でお話をさせていただいた。個人的にとても大好きなプロダクトなので、そのユーザーカンファレンスでお話をさせていただいたということと、そのプロダクトのAzureでの利用促進に微力ながらも貢献できたということは光栄至極である。ここではそのElastic {ON} Tourでの発表で使用したスライドに補足解説を加えて共有させていただく。

    セッションスライド(+デモ動画)

    15分でお届けする Elastic Stack on Azure 設計・構築ノウハウ from Yoichi Kawasaki

    補足解説

    デプロイメント

    AzureでのElastic Stackの利用は当然ながら仮想マシン(VM)を並べてそこにクラスタを構築することになる。残念ながら現時点でマネージドのElasticサービスはAzureには存在しない。VMベースということで特にオンプレと変わらずマニュアルであったり、ChefやAnsibleなどの構成管理ツールを使ってクラスタを組んだり柔軟な構築が可能であるものの、ここではAzureでの構築ということでARMテンプレートを使ったデプロイメントの方法を紹介している。

    • Azure Marketplaceからのデプロイ:最も手っ取り早い方法。30日のX-Packトライアルライセンスが付いていて、トライアル期間が過ぎてもBYOLでライセンスの更新が可能。テンプレートでは2017年12月時点でv2.0.2 〜 v5.6.3の選択が可能。何も考えず最新版をご利用ください。
    • Github上のARMテンプレートをカスタマイズしてデプロイ:Elastic社が用意したGithub上のARMテンプレートがあるのでそれを自分の要件に応じてカスタマイズしてデプロイメントをする。Azure CLIやPowerShellなどコマンドを使ったデプロイメントが可能なので構成管理ツールに組み込んで周辺環境を合わせて自動構築設定も可能。慣れてきたらこちらがよいでしょう。

    推奨仮想ハードウェアとDISK

    Elasticクラスタ全体のパフォーマンスを引き出すためには機能別に適正なVMインスタンスとサイズを選択ください。またVMにアタッチするディスクについてはビルトインで可用性設定がされているManaged Disk、もしくはPremium Managed Diskを選択することをお忘れなく。

    elastic-stack-on-azure-vm-size

    可用性の設定について

    AzureでIaaSで可用性の設定といえばおなじみの可用性セット(Availability Set)と可用性ゾーン(Availability Zone)。当然Elastic Stackのクラスタを組む時もこれらの設定を入れましょうというお話。可用性ゾーンは、その可用性レベルの高さから将来的には可用性ゾーンが主流な設定になっていくはずであるものの、2017年12月時点でPreviewリリースであり、利用可能リージョンが米国東部第2、西ヨーロッパのみというとても限定的なものとなっている。現時点でプロダクション用途となると可用性セット一択なので何も考えずに可用性セットを組んでください。

    可用性セット(Availability Set)

    • 一つのDCの中で同一の物理ラックや電源などを配置しないようにして、障害が発生してもグループの中のどこかのVMは生きているようにする設定のこと
    • VM SLA 99.95%で提供

    可用性ゾーン(Availability Zone)

    • 各VMを別々のゾーンに配置するのでDCレベルの障害につよい(それぞれゾーンは電源、ネットワーク、冷却装置が完全に物理的に分離されたものとなっている)。ちなみに、Azureのリージョンは複数のデータセンターで構成されており、その間を高速なバックボーンで接続して1つのリージョンとして透過的に利用が可能となっているのでこのようなことができるわけだ
    • VM SLA 99.99%で提供
    • 【注意点】可用性ゾーンの設定ではDCが分かれて配置されるので次の2点の考慮が必要:(1)マスターは各ゾーンに分散するように各ゾーン最低1ノード配置すること(2)データノードはゾーンにまたがる通信が極力起こらないように工夫すること。これを実現するのがShard Allocation Awarenessという仕組みで、この仕組みをつかうことで 同一ゾーン内に配置されているノードだけで完全なシャードを保持するようにして、検索要求が同一ゾーン内で完結できるように設定が可能となる

    ネットワークセキュリティグループの設定

    AzureのIaaSにおけるネットワークフィルタリングの設定に、ネットワークセキュリティグループ(NSG)とよばれるL4フィルタリングがある。当然ながら、既にX-Packを導入していればそのセキュリティ機能の1つとしてネットワークレベルのアクセス制御についても行うことができるが、X-Packを導入していない場合は確実にNSGの設定は必要になってくる。また、Elastic Stack以外のアプリケーションとの連携の際にも必ず必要になってくる。Azure上でのシステム構築では欠かすことのできない設定の1つ。

    Azureサービスからのデータコレクション

    elastic-stack-on-azure-data-injestion

    Azure VMについては、オンプレ同様に、ビルトインのBeatsやlogstashとの連携により、そのログやMetricsなどのデータコレクションを実現することができる。一方、Azureが特に力を入れているPaaS(Platform as a Services)からのデータコレクションについてはどうかというと、下記のサービスについては既にビルトインで用意されている機能や、コミュニティ製Logstash Input プラグインを利用することでデータコレクションを実現することができる。 H2M_LI_HEADER Azure Blob Storage: logstash-input-azureblob H2M_LI_HEADER Azure Service Bus (Topic): logstash-input-azuretopic H2M_LI_HEADER Azure Event Hub: logstash-input-azureeventhub H2M_LI_HEADER Azure SQL Database: logstash-input-jdbc H2M_LI_HEADER Azure Database for MySQL: logstash-input-jdbc H2M_LI_HEADER Azure Database for PostgreSQL: logstash-input-jdbc H2M_LI_HEADER Azure HDInsight: ES-Hadoopによる連携

    ちなみに、Azureサービス向けLogstashプラグイン一覧についてはこちら - Logstash plugins for Microsoft Azure Services

    Azure Diagnostics、Activities、Metricsログのコレクション

    elastic-stack-on-azure-metrics-diagnostics-logs

    基本的にIaaS、PaaS問わずAzureのほとんどのサービスから下記の情報が出力され、これらの情報はBlobストレージまたはEvent Hubに出力設定が可能となっている。 H2M_LI_HEADER 診断ログ(Diagnostics log) H2M_LI_HEADER アクティビティログ(Activity log) H2M_LI_HEADER メトリック

    また上記に加えて、PaaSサービスに展開するアプリケーション固有のログについても少なくともBlobストレージに書き込むことはできる。つまるところ、上記データコレクションの項目でご紹介したようにBlobストレージやEvent hubに出力されたデータはLogstashを通じてElastic Stackに取り込むことが可能であり、Azureリソース関連情報を含めた全てのログはElastic Stackで一元管理することができるのである。

  • Azure Functions Python Programming - Experimental

    今年もあと少し。ほぼ趣味の範囲を超えないレベルで今年取り組んだテーマの1つにAzure Functions with Pythonがある。あまり情報が無い中、興味本位でサンプルコードを作っては動かして試して得られた情報をシコシコとGithubに上げているうちにナレッジが溜まって来た。それほど多くはないと思うがPythonでAzure Functionsアプリを作りたいという人もいると思うのでノウハウをブログにまとめておく。いきなり水を差すようではあるが、現時点(2017年12月)ではAzure FunctionsのPythonサポータビリティはExperimental(実験的サポート)でありプロダクション向きではない状況であるので、ホントにPythonが好きな人がOn your own riskで楽しんでいただければと思う。

    Azure FunctionsのPythonサポート状況

    Azure FunctionsのRuntimeには大きく1系と2系の2種類あるが、現時点でPythonは1系でのみExperimentalサポートという状況( See also 言語サポート状況

    azure-functions-runtime-version

    Experimental(実験的サポート)なので本番での利用は非推奨であり、公式サポートはない(ベストエフォートでのサポートは得られるはず)。また、当然ながらGA言語に比べパフォーマンスは悪い。PythonはFunction呼び出し毎にpython.exeが実行される(GA言語はRuntimeと同じプロセスで実行)。

    将来的な話をすると、Azure Functions Runtime 1系でのPythonサポートについては今のExperimentalの域を超えることはないだろう。一方、Runtime 2系ではPythonが正式サポートされるように対応が進められている。ただし時期は未定。この対応については下記Github Issueが切られており、ある程度の対応状況であれば確認可能。Pythonを使う利点の1つに、強力な数理計算、自然言語解析、機械学習系モジュールがあるが、早く安定とパフォーマンスが備わったPythonサーバレスアプリ実行環境でこれら強力なモジュールを活用できたらと思うのは私だけではないだろう。今後の進展に期待。

    Hosting Planの選択について

    Consumption Plan vs App Service Plan

    Azure FunctionsのHosting PlanにはConsumption PlanとApp Service Planの2つがあって、言語に関係なく各プランの特徴は次の通り:

    Consumption Plan

    • コード実行時にコンピューティング割り当て
    • リソース使用量(関数実行時間、使用メモリ)で課金
    • 自動スケール、各処理は〜10分まで

    App Service Plan

    • 専用VMでリソース確保
    • 継続処理:10分以上の処理
    • App Service環境でのみ可能な処理: App Service Environment, VNET/VPN接続, より大きなサイズのVM, etc

    Pythonで使う上で気をつけるポイント

    • Python 3.XなどRuntimeの変更を行う場合は、専用環境である必要があってApp Service Plan必須
    • Consumption Planの場合、Pythonに限らずColdスタート問題という休眠したFunctionの起動が極端に遅くなる問題があるのだが、Pythonの場合は、GA言語に比べてパフォーマンスが悪く、SciPyなど重めのモジュールを利用すると絶望的に遅くなることからConsumption Planでの問題が特に顕著にでてくる。これまでの経験から、小さいインスタンスを並べるConsumption Planよりも比較的大きなサイズのVMが選べるApp Service Planの方が向いていることが多い。Pythonの場合は、予測可能なワークロードに対してApp Service Planで使うほうが問題が少ない。Consumption Planの魅力であるMicro Billing(使った分だけ課金)やリクエストに応じたオートスケーリングといった真のサーバレスに期待される要件は既に正式サポートしているC#、Nodeでやっていただくのがよいかと。

    [参考] Coldスタート問題

    • Consumption Planにおける問題
    • Azure Functions Cold Start Workaround
    • The only downside is that the consumption model that keeps the cost so dirt-cheap means that unless you are using your Function constantly (in which case, you might be better off with the non-consumption options anyway), you will often be hit with a long delay as your Function wakes up from hibernation
    • 休眠したFunctionをどう起こすかがポイント。事前に空リクエストを送ることが考えられるが問題はタイミング(フォーム開いた時とか)

    Python 3.Xランタイムへの変更方法

    2017年12月時点のAzure FunctionsのデフォルトPython Runtimeは2.7.8である。Site ExtensionにPython3.5系とPython3.6系が用意されているので、それを利用してFunctionsで利用するPython Runtimeを変更する方法を下記ページに纏めた。

    モジュールのインストール方法

    pipとKudu DebugConsole/UIを利用した2種類のモジュールインストール方法を下記ページに纏めた。

    Pythonサンプルコード

    私が試したTriggerとBinding利用サンプルコードは全て下記Githubプロジェクトに追加するようにしている。もしこれを読んでいる皆さんで下記プロジェクトでカバーされていないTrigger/Bindingの組み合わせを試されたら是非ともコントリビュートください。

    スライドとHands-onマテリアル

    Azure Antennaにて2017年11月20日11月28日に実施したセッション資料:

    PythonによるAzureサーバレスアプリケーション開発 / Serverless Application Development with Python from Yoichi Kawasaki

    Hands-onマテリアル:

    それでは、Enjoy Serverless Application Development with Python!

    [追記] 上記セッションに関する記事

  • Developing Full Managed Search Application in Azure

    これは9/29 Azure Web Seminar 「Azure サービスを活用して作るフルマネージドな全文検索アプリケーション」のフォローアップ記事です。なかなか暇ができず少々時間が経過してしまいました。

    Azure サービスを活用して作るフルマネージドな全文検索アプリケーション from Yoichi Kawasaki

    Sample Application & Source Code

    セミナーで紹介したサンプルアプリはAzure公式サイトに載せてある代表的なサービスのFAQデータを元にしたHTML/CSS/JavascriptによるQ&Aナレジッジベース検索のシングルページアプリケーションです。検索エンジンにAzure Searchを使い、データソースにCosmos DBを使いAzure SearchのCosmosDB Indexerでクローリングする構成にしてます。ソースコードと設定手順は以下Githubプロジェクトにアップしてあります。もしバグや設定手順等でご質問があればGithubでIssue登録いただければ時間を見つけて対応させていただきます。

    Source Code: https://github.com/yokawasa/azure-search-qna-demo/

    セミナー中に紹介した非構造化データの全文検索デモとして紹介したAI Digital Media Searchアプリケーション。メディア x 音声認識 x 機械翻訳 x 全文検索全てを絡めた面白いアプリケーションなのでこちらでデモ動画とソースコードを共有します。またこのアプリはAzure PaaSサービスを組み合わせてプレゼンテーションレイヤー(Web App for Container)のみならずデータ生成部分(AMS, Functions, Logic App)も全てサーバレスで実現しているのでこのエリアのサンプルアプリとしてもとても良いものになっていると思います。

    AzureSearch.js - Azure Search UIライブラリ

    AzureSearch.jsはAzure SearchのUIライブラリで、Azure Searchプロダクトチーム主要開発者により開始されたOSSライブラリです。TypeScriptで書かれているのでとても読みやすく、また、ライブラリが提供するオブジェクト操作により非常に短いコードでサーチボックス、結果出力、ページネーション、ファセット、サジェスションなどで構成されるサーチ用UIを簡単に組み立てることが可能です。なかなかいけているライブラリにもかかわらず、あまり世の中に知られていないのはもったいないと思いセミナーの最後で紹介させていただきました。これ使わない手はないです。手っ取り早くは、下記のAzureSearch.jsアプリテンプレートジェネレータページで皆さんのAzure SearchアカウントのQueryKeyとインデックススキーマ(JSONフォーマット)を入力するとAzureSearch.jsアプリの雛形が生成されますので、そこから始めるのがよいかと思います。

    END

  • Azure Search Text Analyzer Tools - azure-search-ta

    Azure Searchのアナライザーによるテキスト解析結果を出力する(だけの)ツールを作ってみたのでここで紹介します。その名もazure-search-ta(ta=Test Analyzer)。中身はAzure SearchのAnalyzer APIの出力結果を整形して表示させていているだけの単純なものでありますが、Azure Searchの全文検索チューニングやキーワードにヒットしない原因調査をする際には役に立つと思ってます。「どうしてこのキーワードがひっかからないの?」を突き詰めるには最終的にアナライザのテキスト解析結果と突き合わせる必要があるのと、アナライザーを選択する際にテキスト解析が視覚化されていると判断しやすいだろうと。ツールは2種類で (1)Web UIツールと(2)コマンドラインツール

    Web UI Tool

    azure-search-ta-capture https://github.com/yokawasa/azure-search-ta

    インストールは超簡単。(1)Githubからazure-search-taをclone (2)azure-search-ta/ui 配下のファイルをPHPが動くWebサーバにコピー (3)analyze-api.phpをエディタで開いてお使いのAzure Searchカウント名とAzure Search API Adminキーの値を設定ください。あとはazure-search-ta-ui.htmlにアクセスいただければ上記のようなUIが出力されるはずです。なぜHTML/JSだけではなく間にPHPを挟んでいるのかについて、Azure SearchのAnalyze APIや管理系APIリクエストに位置付けられており、管理系APIはvia CORSでのリクエストを受け付けていないからである。

    $ git clone https://github.com/yokawasa/azure-search-ta.git`
    $ vi azure-search-ta/ui/analyze-api.php
    
    $azureSearchAccount="";
    $azureSearchApiKey = "";
    

    Command-Line Tool

    1. インストールと設定

    pipでazure-search-taパッケージをインストール。既に古いバージョンをインストール済みでアップデートする際は――upgradeをつけて実行ください。

    $ pip install --user azure-search-ta
    

    次に、search.confにお使いのAzure Searchカウント名とAzure Search API Adminキーの値を設定ください。

    # Azure Search Service Name ( never put space before and after = )
    SEARCH_SERVICE_NAME=
    # Azure Search API Admin Key ( never put space before and after = )
    SEARCH_API_KEY=
    

    2. 使い方

    いくつか使い方を紹介します。

    usage: azure-search-ta [-h] [-v] [-c CONF] [-i INDEX] [-a ANALYZER]
                              [-t TEXT] [-o OUTPUT]`
    
    This program do text analysis and generate formatted output by using Azure
    Search Analyze API
    
    optional arguments:
      -h, --help            show this help message and exit
      -v, --version         show program's version number and exit
      -c CONF, --conf CONF  Azure Search Configuration file. Default:search.conf
      -i INDEX, --index INDEX
                            Azure Search index name
      -a ANALYZER, --analyzer ANALYZER
                            Azure Search analyzer name
      -t TEXT, --text TEXT  A file path or HTTP(s) URL from which the command line
                            reads the text to analyze
      -o OUTPUT, --output OUTPUT
                            Output format ("simple" or "normal"). Default:normal
    

    ja.microsoftアナライザーによるテキスト解析結果をnormalモードで出力する例。Analyzer APIはパラメータにインデックス名が必要なので、なんでもいいのでご自分のアカウントに設定されているインデックス名を指定ください。ここではインデックスtaでsimple.txtに対象のテキストを記入して実行しています。

    $ cat sample1.txt
    吾輩は猫である
    
    $ azure-search-ta -c ./search.conf -i ta -a ja.microsoft --t sample1.txt
    INPUT: 吾輩は猫である
    TOKENS: [吾輩] [猫] [ある]
    

    Azure Searchにビルトインされているアナライザーでけでなく皆さんが作成したカスタムアナライザーによるテキスト解析結果も当然出力可能です。以下は、インデックスtacustomにNグラム分割のカスタムアナライザーmy_ngramを作成したとしてmy_ngramアナライザーによるテキスト解析結果を出力する例です。カスタムアナライザーの定義はGithubページのほうに詳しく書いているのでよかったらどうぞ。

    $ cat sample1.txt
    吾輩は猫である
    
    $ azure-search-ta -c ./search.conf -i tacustom -a my_ngram --t sample1.txt -o simple
    '吾輩' '吾輩は' '吾輩は猫で' '吾輩は猫' '輩は猫であ' '輩は' '輩は猫' '輩は猫で' 'は猫であ' 'は猫で' 'は猫' 'は猫である' '猫であ' '猫で' '猫で ある' 'である' 'であ' 'ある'
    

    他には、azure-search-taはインターネット上のページのテキスト解析機能も付いているので、そいつを試してみます。ja.luceneアナライザを使ってhttp://www.yahoo.co.jpトップページの内容を解析します。

    $ azure-search-ta -c ./search.conf -i ta -a ja.lucene --t http://www.yahoo.co.jp -o simple
    'html' 'public' 'w' '3' 'c' 'dtd' 'html' '4' '01' 'transitional' 'en' 'http' 'www' 'w' '3' 'org' 'tr' 'html' '4' 'loose' 'dtd' 'yahoo' 'japan' 'ヘルプ' 'yahoo' 'japan' 'トップページ' '機能' '正しく' 'ご' '利用' 'いただく' '下記' '環境' '必要' 'windows' 'internet' 'explorer' '9' '0' '以上' 'chrome' '最新' '版' 'firefox' '最新' '版' 'microsoft' 'edge' 'macintosh' 'safari' '5' '0' '以上' 'internet' 'explorer' '9' '0' '以上' 'ご' '利用' '場合' 'internet' 'explorer' '互換' '表示' '参考' '互換' '表示' '無効' '化' '試し' 'くださる' '東北' '自転車' 'イベント' '参加' '方法' '事前' 'チェック' 'こだわり' 'ご' '当地' 'スイーツ' '取り寄せる' '上海' 'マージャン' '定番' 'ゲーム' '無料' '遊ぶ' 'ニュース' '11' '時' '1' '分' '更新' '韓国' '北' '太陽' '政策' '回帰' '娘' '放置' '熱中' '症' '死なす' '逮捕' '保育' '死亡' '事故' '睡眠' '中' '注意' '三菱' 'ufj' '法人' '融資' '銀行' '集約' 'フレーバ' '水' '人気' '続く' '各国' '大' '規模' 'サイバ' '攻撃' '西武' '菊池' '沢村' '超' '驚異' '被' '打率' '寺島' 'しのぶ' '長男' '超' '英才' '教育' 'もっと' '見る' '記事' '一覧' 'ゴミ' '収集' '車' '子育て' '5' '月' '13' '日' '7' '時' '55' '分' '配信' '産経新聞' 'ショッピング' 'ヤフオク' '旅行' 'ホテル' '予約' 'ニュース' '天気' 'スポーツナビ' 'ファイナンス' 'テレビ' 'gyao' 'y' 'モバゲ' '地域' '地図' '路線' '食べる' 'ログ' '求人' 'アルバイト' '不動産' '自動車' '掲示板' 'ブログ' 'ビューティ' '出会い' '電子' '書籍' '映画' 'ゲーム' '占い' 'サービス' '一覧' 'ログイン' 'id' 'もっと' '便利' '新規' '取得' 'メール' 'メールアドレス' '取得' 'カレンダ' 'カレンダ' '活用' 'ポイント' '確認' 'ログイン' '履歴' '確認' '会社' '概要' '投資' '家' '情報' '社会' '的' '責任' '企業' '行動' '憲章' '広告' '掲載' '採用' '情報' '利用' '規約' '免責' '事項' 'メディア' 'ステートメント' 'セキュリティ' '考え方' 'プライバシ' 'ポリシ' 'copyright' 'c' '2017' 'yahoo' 'japan' 'corporation' 'all' 'rights' 'reserved'
    

    もう少しだけGithubのほうには詳しく書いてあるのでそちらも参考にしてください。

  • Python Easter Egg

    Python Easter Egg = Pythonの隠しクレジットとはいってもPython基礎本などでよく紹介されているものなので既にご存知かもしれないが背景が面白いのでここで紹介。

    Pythonにはthisモジュールという「The Zen of Python」(Note 1)を出力するだけのモジュールがある。このモジュール、中身(Note 2)を見てみると分かるが、総ステップにしてわずか28行、ROT13暗号化(Note 3)された文字列を復号化するだけの単純で取るに足らないものかもしれないがこのモジュールが作られた背景は面白い。Barry Warsaw氏が記事「import this and The Zen of Python」でthisモジュールが誕生にまつわる面白い話を紹介している。

    import this and The Zen of Python」の一部簡訳

    2001年秋、Foretec Seminar社はのInternational Python Conference #10(以下IPC10、Pyconの前身となるカンファレンス)の準備をしておりPythonコミュニティからそのカンファレンスのスローガンを求めていた。スローガンはTシャツにもプリントされる予定だった。Guideや、Fred、Jeremyや著者達はかつてはForetec Seminar社に所属していたがPythonlabsを結成する2000年に同社を去っている。そしてPythonlabsはPythonコミュニティからのスローガン応募の審査と勝者の選定を担当することになった。応募は500くらいあったが、どれもひどいものだった。Timと著者は1つに絞られるまで何度となく選別作業を行い

    最終的に”import this”を選んだ。理由は”import this”という言葉の持つふざけた、小バカにしたようなトーンが好きだったからという。

    著者たちはこの”import this”をスローガンに選んですぐにthisモジュール(this.py)を実装した。モジュールは「The Zen of Python」を出力するだけのものだったが途中TimやGuidoの提案でrot13で暗号化して内容を少し難読化する工夫がされたりもした。IPC10が終わってすぐ、彼らはこのイベントを記念してthisモジュールをPython2.2.1ブランチにコミットした。この時、著者の提案で他の誰にも知られないようにするためにソース管理システムのチェックイン通知機能を停止し、こっそりこのモジュールをPython2.2.1のブランチに含めたのだ。これらのことは彼ら以外に誰にも知らせず内緒で行われた。著者いわく、この彼らの仕込んだeaster egg(thisモジュールのこと。ソフトウェアでいうeaster eggとは隠しコマンドとか、隠しクレジットのようなもの)が誰かに見つかるまではしばらく時間がかかったそうだ。

    Barry Warsaw氏が同記事を「That was all back in the day when the Python community had a sense of humor」という一文で締めくくっているように、この記事を読むと当時のPythonコミュニティがいかにユーモア溢れたものだったのかが感じられる。phython-2.2.1がリリースされたのは2002年4月10日で、それからどれくらい経ってこのthisモジュールが発見されたのか分からないが初めて発見した人は絶対ほっこりしたことだろう。

    Note 1: import this

    The Zen of Python」はPythonハッカー、Tim Petersによって書かれた有名な文章でPython設計哲学を要約したようなものと言われている。 Barry Warsaw氏の記事によると起源はTim Peters氏による1999年6月4日のPython-listへのこの投稿のようだ。以下、Pythonインタラクティクモードでimport thisを実行し「The Zen of Python」を表示させた内容:

    $ python
    Python 3.4.3 (default, Oct 14 2015, 20:28:29)
    [GCC 4.8.4] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import this
    The Zen of Python, by Tim Peters
    
    Beautiful is better than ugly.
    Explicit is better than implicit.
    Simple is better than complex.
    Complex is better than complicated.
    Flat is better than nested.
    Sparse is better than dense.
    Readability counts.
    Special cases aren't special enough to break the rules.
    Although practicality beats purity.
    Errors should never pass silently.
    Unless explicitly silenced.
    In the face of ambiguity, refuse the temptation to guess.
    There should be one-- and preferably only one --obvious way to do it.
    Although that way may not be obvious at first unless you're Dutch.
    Now is better than never.
    Although never is often better than *right* now.
    If the implementation is hard to explain, it's a bad idea.
    If the implementation is easy to explain, it may be a good idea.
    Namespaces are one honking great idea -- let's do more of those!
    >>>
    

    Note 2: this.py

    this.pyの中身。 意味不明なコードをROT13 (Note 3)で複合化することで「The Zen of Python」を出力している。 /usr/lib/python3.4/this.py

    s = """Gur Mra bs Clguba, ol Gvz Crgref
    
    Ornhgvshy vf orggre guna htyl.
    Rkcyvpvg vf orggre guna vzcyvpvg.
    Fvzcyr vf orggre guna pbzcyrk.
    Pbzcyrk vf orggre guna pbzcyvpngrq.
    Syng vf orggre guna arfgrq.
    Fcnefr vf orggre guna qrafr.
    Ernqnovyvgl pbhagf.
    Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
    Nygubhtu cenpgvpnyvgl orngf chevgl.
    Reebef fubhyq arire cnff fvyragyl.
    Hayrff rkcyvpvgyl fvyraprq.
    Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
    Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
    Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
    Abj vf orggre guna arire.
    Nygubhtu arire vf bsgra orggre guna *evtug* abj.
    Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
    Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
    Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""
    
    d = {}
    for c in (65, 97):
        for i in range(26):
            d[chr(i+c)] = chr((i+13) % 26 + c)
    
    print "".join([d.get(c, c) for c in s])
    

    Note 3: ROT13

    ROT13は定められた置き換えマップにもとづいて文字を置き換えるだけの単純な暗号方式。次の変換マップに基づいて文字を変換するので例えばA→N、B→O、C→Pのように変換される。

    ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
                     ↑↓
    NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm
    

    ちなみにthis.pyではこの変換マップをモジュール中で生成しているがPython2系、3系ではROT13の実装は標準で組み込まれているので次のように直接decode関数に’rot13’を指定することでROT13で暗号化された文字列sを複合化することができる。

    >>> this.s
    "Gur Mra bs Clguba, ol Gvz Crgref\n\nOrnhgvshy vf orggre guna htyl.\nRkcyvpvg vf orggre guna vzcyvpvg.\nFvzcyr vf orggre guna pbzcyrk.\nPbzcyrk vf orggre guna pbzcyvpngrq.\nSyng vf orggre guna arfgrq.\nFcnefr vf orggre guna qrafr.\nErnqnovyvgl pbhagf.\nFcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.\nNygubhtu cenpgvpnyvgl orngf chevgl.\nReebef fubhyq arire cnff fvyragyl.\nHayrff rkcyvpvgyl fvyraprq.\nVa gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.\nGurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.\nNygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.\nAbj vf orggre guna arire.\nNygubhtu arire vf bsgra orggre guna *evtug* abj.\nVs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.\nVs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.\nAnzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"
    
    >>> this.s.decode('rot13')
    u"The Zen of Python, by Tim Peters\n\nBeautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.\nReadability counts.\nSpecial cases aren't special enough to break the rules.\nAlthough practicality beats purity.\nErrors should never pass silently.\nUnless explicitly silenced.\nIn the face of ambiguity, refuse the temptation to guess.\nThere should be one-- and preferably only one --obvious way to do it.\nAlthough that way may not be obvious at first unless you're Dutch.\nNow is better than never.\nAlthough never is often better than *right* now.\nIf the implementation is hard to explain, it's a bad idea.\nIf the implementation is easy to explain, it may be a good idea.\nNamespaces are one honking great idea -- let's do more of those!"
    

    おわり

  • azuresshconfig has been dockerized

    UPDATED 2017-02-15: changed docker run command example due to Issue#4

    以前「azuresshconfigの紹介 – Azure上でのSSH生活を少しだけ快適にする」の投稿でazuresshconfigの紹介をさせていただいたが、ツールをリリースして以来、数少ない貴重な利用者様からインストールがコケるんだけど何とかしろというクレームをいただいていた。そこでインストールマニュアルを充実させようかとか、インストーラーをプラットフォーム別に充実させようかとか考えたものの、ここは流行りのコンテナ実行できるようしたほうがいいだろうということでDocker対応することにした。

    今回の対応によりpipインストールや、プラットフォーム別にprerequisiteなランタイム、ヘッダファイル、ライブラリといった面倒なインストールが不要となり、Mac、Windows、Linux(Ubuntu、CentOS、その他distro)関係なくシンプルにdocker runコマンドでの実行が可能となった。

    しかも超軽量LinuxディストリビューションであるAlpine Linuxの上にPythonランタイムとツールを載せているだけであるためサイズはたったの155MBとかなり軽め

    $ docker images azuresshconfig
    
    REPOSITORY         TAG         IMAGE ID            CREATED             SIZE
    azuresshconfig    latest     7488bef4343f        7 minutes ago       155 MB
    

    実行例

    $ docker run -v $HOME:/root --rm -it yoichikawasaki/azuresshconfig \
        --output stdout --user yoichika --identityfile ~/.ssh/id_rsa > $HOME/.ssh/config
    

    Dockerfileをダウンロードしてビルド・実行はこちら

    $ curl https://raw.githubusercontent.com/yokawasa/azure-ssh-config/master/Dockerfile -o Dockerfile
    $ docker build -t azuresshconfig .
    $ docker run -v $HOME:/root --rm -it yoichikawasaki/azuresshconfig \
        --output stdout --user yoichika --identityfile ~/.ssh/id_rsa > $HOME/.ssh/config
    

    Enjoy SSH life on Azure with dockerized azuresshconfig!

  • Logstash plugins for Microsoft Azure Services

    Logstash is an open source, server-side data processing pipeline that ingests data from a multitude of sources simultaneously, transforms it, and then sends it to your favorite destinations. Here is a list of logstash plugins for Microsoft Azure Services.

    Plugin Name Target Azure Services Note
    logstash-input-azureeventhub EventHub Logstash input plugin reads data from specified Azure Event Hubs
    logstash-input-azureblob Blob Storage Logstash input plugin that reads and parses data from Azure Storage Blobs
    logstash-input-azuretopic Service Bus Topic Logstash input plugin reads messages from Azure Service Bus Topics
    logstash-input-azuretopicthreadable Service Bus Topic Logstash input plugin reads messages from Azure Service Bus Topics using multiple threads
    logstash-output-applicationinsights Application Insights Logstash output plugin that store events to Application Insights
    logstash-input-azurewadtable Table Storage Logstash input plugin for Azure Diagnostics. Specifically pulling diagnostics data from Windows Azure Diagnostics tables
    logstash-input-azurewadeventhub EventHub Logstash input plugin reads Azure diagnostics data from specified Azure Event Hubs and parses the data for output
    logstash-input-azurewadtable Table Storage Logstash input plugin reads Azure diagnostics data from specified Azure Storage Table and parses the data for output
    logstash-output-documentdb Cosmos DB logstash output plugin that stores events to Azure Cosmos DB
    logstash-output-azuresearch Azure Search logstash output plugin that stores events to Azure Search
    logstash-output-azure_loganalytics Log Analytics logstash output plugin that stores events to Azure Log Analytics
    logstash-input-jdbc SQL Database, Azure Database for MySQL/PostgreSQL Input plugin to ingest data in any database with a JDBC interface into Logstash that support most of major RDBMS such as MySQL、PostgreSQL、OracleDB、Microsoft SQL, etc

    (as of Dec 29, 2016)

    logstash

  • Detecting faces in Video contents using Azure Cognitive Services Face API

    過去に本ブログでビデオコンテンツを切り口とした音声認識OCR技術を利用したデモを紹介したが、ここではビデオコンテンツの中の人物出現箇所に連動して人物名を字幕で表示させるデモとその実装方法を紹介したい。人物識別にはAzureのCognitive ServicesのFace APIを使っていて、これで動画の中に出現する顔の検出を行い、予め登録している人物リストとのマッチングにより実現している。 Cognitive Serivcesとは視覚、音声、言語、知識などマイクロソフトがこれまで研究を通じて開発してきたさまざまな要素技術をAPIとして提供しているサービスのことで、最近巷で人工知能(AI)だとかインテリジェンスとかいうキーワードをよく耳にするのではないかと思うがAzure利用シナリオでそういったインテリジェンス(知能/知性)を兼ね備えたアプリを作る場合は間違いなく中核となるサービスの1つである。Face APIはその中でも顔の検出・識別や、顔にまつわる感情、特徴などメタデータ抽出に特化したAPIである。

    Video Summarization and Face Detection Demo Screenshot

    主要テクノロジーと機能

    下図は今回のデモ作成のために行っている処理フローと主要テクノロジーを表している。やっていることは大きく分けて3つ: (1) 動画コンテンツをAzure Media Encoder Standardを使ってフレームごとの静止画像の作成, (2) Cognitive ServicesのFace APIを使って1より得られた静止画像から顔の検出を行い予め登録している人物リストとマッチング(最も類似度が高いものを本人とみなす)して人物を識別, (3) 2で得られた各フレーム中の人物情報を時間順に並べて字幕(Closed Caption)用のデータファイルを生成。以下、各処理の詳細について説明する。

    VideoFramesFaceFecognition_case1

    1. Azure Media Encoder Standardでフレームごとの静止画生成

    残念ながらFace APIはビデオコンテンツから直接顔検出することができないため、一旦ビデオコンテンツから各フレームごとの静止画を生成してその静止画を対象に処理を行う必要がある。ここでは各フレームごとの静止画生成にAzure Media Encoder Standard(MES)を利用する。MESを使うことでエンコードタスクとしてビデオコンテンツに対して様々な処理を行うことができるのだが、MESにはそのエンコードタスクの1つとしてサムネイル生成のためのタスクが用意されており、今回はこのサムネール生成タスクを利用する。他のエンコードタスク同様にサムネイル生成タスクについてもプリセットと呼ばれるエンコードに必要な情報を記述した XML または JSON形式ファイルを用意する必要がある。今回は1秒フレームごとにJPEG形式の静止画(サムネイル)を生成するために次のようなプリセット(amsmp-thumbnail-config.json)を用意した。

    {
      "Version": 1.0,
      "Codecs": [
        {
          "Start": "00:00:00",
          "Step": "00:00:01",
          "Type": "JpgImage",
          "JpgLayers": [
            {
              "Quality": 90,
              "Type": "JpgLayer",
              "Width": 640,
              "Height": 360
            }
          ]
        }
      ],
      "Outputs": [
        {
          "FileName": "{Basename}_{Index}{Extension}",
          "Format": {
            "Type": "JpgFormat"
          }
        }
      ]
    }
    

    MESによるサムネイル処理実行方法やプリセットの詳細については「Media Encoder Standard を使用した高度なエンコード」や同ページの「サムネイルを生成する」項を参照ください。尚、今回のサムネイル生成のためのエンコーディング処理は小生自作の「azure-media-processor-java」を利用してバッチ実行している。

    2. Cognitive Services Face APIによる顔の検出と人物の識別

    ここではCognitive ServicesのFace APIを使って1で得られたフレームごとの静止画像に対して顔検出を行い、予め登録している人物リスト(Face APIでいうところのPerson Group)と比較して最も類似度の高い人物(Face APIでいうところのPerson )をその本人として識別する。

    2-1. 人物リスト(Person Group)の作成

    人物リスト(Person Group)の作成で必要な作業とFace APIの利用インターフェースは次の通り:

    • Create a Person Group APIを使って Person Groupを作成
    • 上記で作成したPerson Groupの中にCreate a Person APIで人物ごとにPersonを作成する。作成されたPerson対してAdd a Person Face APIでその人物の顔画像を登録する。Face APIでは各Personに最大248枚の顔画像を登録が可能となっており、さまざまな種類の顔を登録することで機械学習によりその人物の顔識別の精度が向上するとされている。
    • 上記でPerson Groupに対して登録されたPersonデータ(Personごとの顔データ)は最終的にTrain Person Group APIでトレーニングされることで、次の2-2で行う顔識別(Face Identify API)処理で利用可能なデータとなる。注意点として、いくらある人物の顔画像を登録したとしてもそれがトレーニングされない限り顔識別処理において有効にはならないため、新しく顔を登録した場合はトレーニング処理を忘れずに行ってください(この手のことは自動化しておいてください)。

    2-2. 静止画像中の顔認識と人物識別

    ここで行う処理の流れとFace APIの利用インターフェースは次の通り:

    • Face Detect APIを使って静止画像中の顔を検出する。検出された顔ごとに固有のIDが得られる。尚、1枚の画像で複数の顔が検出された場合、最大64までは取得可能となっている(2016年12月現在)。
    • 上記の顔検出で得られた顔IDを元にFace Identify APIを使って2-1で登録した人物リスト(Person Group)に対して人物検索を行い顔の類似度(0~1の数値)が高いもの順に一覧を取得することができる。ここでは最も類似度が高い人物をその顔の人物として決定する。

    3. 字幕(Closed Caption)データファイルの生成

    2で得られた各フレーム中の人物情報と各フレームの時間を元に字幕用のデータフォーマットであるWebVTTフォーマットファイルを生成する。以下、6秒~30秒までの字幕出力を期したWebVTTファイルのサンプルであるが、見ていただいてわかる通りフレームの時間(最小秒単位)とそこで得られた人物名をセットで記述するとても単純なフォーマットとなっている。

    00:00:06.000 --> 00:00:07.000
    Satya Nadella(0.73295)`
    
    00:00:07.000 --> 00:00:08.000
    Satya Nadella(0.6313)
    
    00:00:27.000 --> 00:00:28.000
    Bryan Roper(0.68094)
    
    00:00:29.000 --> 00:00:30.000
    Bryan Roper(0.54939)
    

    各フレームの時間について、今回のビデオコンテンツのフレームは1秒ごとに取得しており、フレームごとの静止画像ファイルにはフレームの順番がPostfixとしてファイル名に含まれているため単純にファイル名からフレームの時間が特定できるようになっている(例, 10番目のファイル= videoassetname_000010.jpg)。もし今回のような機械的なルールがない場合はフレーム用画像ファイル名と時間のマッピングが必要となる。

    ビデオコンテンツと字幕の再生は「ビデオコンテンツの音声認識デモ」でも紹介したようにHTML5のtrackタグエレメントによるビデオファイルの字幕表示機能使って人物名の字幕表示を実現している。本デモではHTML5に下記のようにビデオファイル(MP4)をVideoソースとしてtrackエレメントにWebVTTファイル(build2016keynote.vtt)を指定している。

    <video id="Video1" controls autoplay width="600">
        <source src="KEY01_VideoThumbnail.mp4" srclang="en" type="video/mp4">
        <track id="trackJA"  src="build2016keynote.vtt"  kind="captions" srclang="ja" label="Person Name" default>
    </video>
    

    デモデータ作成手順

    GithubプロジェクトページVideoFramesFaceRecognition-Pythonの1. Preparationと2. Batch executionを実施いただければFace APIで識別した各フレームごとの人物名を元に字幕データ*.vttファイルが生成されデモページ表示のための準備は完了する。最後に表示用の静的ページを生成すれば完了。本デモの表示用ページデータはこちらで、基本的にindex.htmlの変更のみでいけるはず。

    本デモコンテンツについて何か問題を発見した場合はこちらのGithub IssueページにIssueとして登録いただけると幸いである。

    Azure Media Analytics Face Detectorを活用した処理の効率化

    今回の人物識別ではビデオコンテンツの全てのフレームに対してFace APIを使って顔検出処理を行っているが、これでは顔出現フレームが少ないコンテンツの場合には無駄なFace APIリクエストが大量に発生してしまうため効率的な処理とは言えない。ということで、ここではAzure Media Face Detectorを活用して処理を効率化する方法を紹介したい。

    Azure Media Face DetectorはAzure Media Servicesのメディアプロセッサ(MP)の1つで、ビデオコンテンツから顔の検出や感情の検出をすることができる。残念ながらAzure Media Face DetectorはFace APIのように顔の識別を行うことはできないものの、ビデオコンテンツから直接顔を検出することができる、即ちビデオコンテンツから直接顔が存在するフレームを特定することができる。よって、この機能を利用して一旦Azure Media Face Detectorで顔が検出されたフレームのみに絞り込んでからFace APIを使ってフレームの静止画像に対して顔検出・顔識別を行うことで無駄なFace APIリクエストを減らして処理の効率化を図ることができる。処理フローとしては次のようなイメージ。

    VideoFramesFaceFecognition_case2

    おまけ: Video Summarization

    デモページをみていただくとお分かりのように今回のデモでは3分のビデオコンテンツを題材としているが、元ネタはChannel9で公開されている計138分のBuild 2016のキーノートセッションである。このキーノートのセッションはデモコンテンツとしてはあまりに長かったのでこれをAzure Media Video Thumbnailsメディアプロセッサ(MP)を使って3分に要約している。Azure Media Video Thumbnailsはアルゴリズムベースで特徴シーンの検出とそれらを結合(サブクリップ)してビデオコンテンツを指定した長さに要約することができるMPで、現在Public Previewリリース中(2016年12月現在)。

    参考までに、要約(3分:180秒)に使用したAzure Media Video Thumbnailsのタスクプリセットは以下の通り:

    {
        "version": "1.0",
        "options": {
            "outputAudio": "true",
            "maxMotionThumbnailDurationInSecs": "180",
            "fadeInFadeOut": "true"
        }
    }
    

    END

  • Collecting events into Azure Functions and triggering your custom code using fluent-plugin-azurefunctions

    In this article, I’d like to introduces a solution to collect events from various sources and send them into HTTP Trigger function in Azure Functions using fluent-plugin-azurefunctions. Triggers in Azure Functions are event responses used to trigger your custom code. HTTP Trigger functions allow you to respond to HTTP events sent from fluentd and cook them into whatever you want!

    fluent-plugin-azurefunctions

    [note] Azure Functions is a (“serverless”) solution for easily running small pieces of code, or “functions,” in Azure. Fluentd is an open source data collector, which lets you unify the data collection and consumption for a better use and understanding of data. fluent-plugin-azurefunctions is a fluentd output plugin that enables to collect events into Azure Functions.

    Pre-requisites

    • A basic understanding of fluentd - if you’re not familiar with fluentd, fluentd quickstart guide is good starting point
    • Azure subscription - you need to have Azure subscription that grants you access to Microsoft Azure services, and under which you can create Azure Functions account. If you don’t have yet click here to create it

    Setup: Azure Functions (HTTP Trigger Function)

    Create a function (HTTP Trigger). First, you need to have an function app that hosts the execution of your functions in Azure if you don’t already have. Once you have an function app, you can create a function. Here are instructions:

    A quick-start HTTP trigger function sample is included under examples/function-csharp in Github repository. You simply need to save the code (run.csx) and configuration files (function.json, project.json) in the same Azure function folder. Explaining a little bit about each of files, the function.json file defines the function bindings and other configuration settings. The runtime uses this file to determine the events to monitor and how to pass data into and return data from function execution. The project.json defines packages that the application depends. The run.csx is a core application file where you write your code to process Your jobs. Here is a sample run.csx:

    using System.Net;
    using Newtonsoft.Json;
    
    public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
    {
        log.Info("C# HTTP trigger function to process fluentd output request.");
        log.Info( string.Format("Dump request:\n {0}",req.ToString()));
        // parse query parameter
        string payload = req.GetQueryNameValuePairs()
            .FirstOrDefault(q => string.Compare(q.Key, "payload", true) == 0)
            .Value;
        // Get request body
        dynamic data = await req.Content.ReadAsAsync<object>();
        if (data.payload == null) {
            log.Info("Please pass a payload on the query string or in the request body");
            return new HttpResponseMessage(HttpStatusCode.BadRequest);
        }
        // Process Your Jobs!
        dynamic r = JsonConvert.DeserializeObject<dynamic>((string)data.payload);
        if (r.key1!=null) log.Info(string.Format("key1={0}",r.key1));
        if (r.key2!=null) log.Info(string.Format("key2={0}",r.key2));
        if (r.key3!=null) log.Info(string.Format("key3={0}",r.key3));
        if (r.mytime!=null) log.Info(string.Format("mytime={0}",r.mytime));
        if (r.mytag!=null) log.Info(string.Format("mytag={0}",r.mytag));
        return new HttpResponseMessage(HttpStatusCode.OK);
    }
    

    Setup: Fluentd

    First of all, install Fluentd. The following shows how to install Fluentd using Ruby gem packger but if you are not using Ruby Gem for the installation, please refer to this installation guide where you can find many other ways to install Fluentd on many platforms.

    # install fluentd
    $ sudo gem install fluentd --no-ri --no-rdoc
    
    # create fluent.conf
    $ fluentd --setup 
    

    Also, install fluent-plugin-azurefunctions for fluentd aggregator to send collected event data into Azure Functions.

    $ sudo gem install fluent-plugin-azurefunctions
    

    Next, configure fluent.conf, a fluentd configuration file as follows. Please refer to this for fluent-plugin-azurefunctions configuration. The following is a sample configuration where the plugin writes only records that are specified by key_names in incoming event stream out to Azure Functions:

    # This is used by event forwarding and the fluent-cat command
    <source>
        @type forward
        @id forward_input
    </source>
    
    # Send Data to Azure Functions
    <match azurefunctions.**>
        @type azurefunctions
        endpoint  AZURE_FUNCTION_ENDPOINT   # ex. https://<accountname>.azurewebsites.net/api/<functionname>
        function_key AZURE_FUNCTION_KEY     # ex. aRVQ7Lj0vzDhY0JBYF8gpxYyEBxLwhO51JSC7X5dZFbTvROs7uNg==
        key_names key1,key2,key3
        add_time_field true
        time_field_name mytime
        time_format %s
        localtime true
        add_tag_field true
        tag_field_name mytag
    </match>
    

    [note] If key_names not specified above, all incoming records are posted to Azure Functions (See also this).

    Finally, run fluentd with the fluent.conf that you configure above.

    $ fluentd -c ./fluent.conf -vv &
    

    TEST

    Let’s check if test events will be sent to Azure Functions that triggers the HTTP function (let’s use the sample function included in Github repo this time). First, generate test events using fluent-cat like this:

    echo ' { "key1":"value1", "key2":"value2", "key3":"value3"}' | fluent-cat azurefunctions.msg
    

    As both add_time_field and add_tag_field are enabled, time and tag fields are added to the record that are selected by key_names before posting to Azure Functions, thus actual HTTP Post request body would be like this:

    {
        "payload": '{"key1":"value1", "key2":"value2", "key3":"value3", "mytime":"1480195100", "mytag":"azurefunctions.msg"}'
    }
    

    If events are sent to the function successfully, a HTTP trigger function handles the events and the following logs can be seen in Azure Functions log stream:

    2016-11-26T21:18:55.200 Function started (Id=5392e7ae-3b8e-4f65-9fc1-6ae529cdfe3a)
    2016-11-26T21:18:55.200 C# HTTP trigger function to process fluentd output request.
    2016-11-26T21:18:55.200 key1=value1
    2016-11-26T21:18:55.200 key2=value2
    2016-11-26T21:18:55.200 key3=value3
    2016-11-26T21:18:55.200 mytime=1480195100
    2016-11-26T21:18:55.200 mytag=azurefunctions.msg
    2016-11-26T21:18:55.200 Function completed (Success, Id=5392e7ae-3b8e-4f65-9fc1-6ae529cdfe3a)
    

    Advanced Senarios

    1. Near Real-time processing

    Function Apps can output messages to different means or data stores. For example, fluentd collects events generated from IoT devices and send them to Azure Function, and the the HTTP trigger function transforms the events and processes the data to store in a persistent storage or to pass them to different means. Here are some of options available at the time of writing:

    2. Background jobs processing

    If the jobs are expected to be large long running ones, it’s recommended that you refactor them into smaller function sets that work together and return fast responses. For example, you can pass the HTTP trigger payload into a queue to be processed by a queue trigger function. Or if the payload is too big to pass into the queue, you can store them onto Azure Blob storage at first, then pass only limited amount of the data into a queue just to trigger background workers to process the actual work. These approaches allow you to do the actual work asynchronously and return an immediate response.

    END

  • Video OCR using Azure Media & Cognitive

    OCRとはOptical Character Recognitionの略で日本語にすると光学文字認識と訳されており、ざっくりと画像の中の文字をテキストに変換する技術のことを指す。テキストに変換されるということは勘が鋭い皆さんはお気づきだと思うが、テキストの全文検索であったり、テキストから音声への変換、さらには機械翻訳を使って多言語への変換といった展開が考えられる。そんな可能性を秘めたOCRであるが、ここではそのOCRの技術を使ってビデオファイルから抽出したテキストデータを元にビデオに字幕表示したり、動画中に表示される文字を全文検索をするデモを紹介したい。内容的には「Azure Media & Cognitiveデモ:Speech-To-Text」で紹介したデモのOCR版といったところ。

    Video OCR Demo Screenshot

    主要テクノロジーと機能

    Azure Media OCRメディアプロセッサによるテキスト抽出

    このデモではAzure Media OCRメディアプロセッサー(MP)を使用してビデオファイル内のテキストコンテンツを検出してテキストファイルを生成している。OCRメディアプロセッサーは入力パラメータによりビデオ解析の挙動を調整することができる。主なパラメータとしては検索対象テキストの言語(日本語もサポート)、テキストの向き、サンプリングレート、ビデオフレーム内のテキスト検出対象のリージョンがあるが、本デモでの入力パラメータ(Video-OCR-Search-Python/src/ocr-detectregion.json)は以下の通り検索対象言語は日本語、1秒おきのサンプリングレート、テキスト検出対象のリージョンからビデオフレーム内の上部1/4を省く設定(検出対象をフレームトップから85 pixel以下を対象)にしている。

    {
        "Version":"1.0", 
        "Options": 
        {
            "Language":"Japanese", 
            "TimeInterval":"00:00:01.000",
            "DetectRegions":
            [
                {"Left":"0","Top":"85","Width":"1280","Height":"635"}
            ]
        }
    }
    

    そして、Azure Media OCRメディアプロセッサはビデオで検出された文字を下記のような表示時間に基づいてセグメント化された形で結果出力する。結果ファイルの完全版はこちら(azuresubs.json)を参照ください。

    {
        "fragments": [
            {
                "start": 0
                "interval": 319319,
                "duration": 319319,
                "events": [
                    [
                        {
                            "language": "Japanese",
                            "text": "Azure の 契 約 内 容 を 変 更 す る Microsoft Azure"
                        }
                    ]
                ]
            },
            {  /* fragment1 */ },
            {  /* fragment2 */ },
            ...
            {  /* fragmentN */ }
        ],
        "version": 1, 
        "framerate": 29.97,
        "height": 720,
        "width": 1280,
        "offset": 0,
        "timescale": 30000
    }
    

    入力パラメータと出力形式共に詳細はこちらのドキュメントを参照いただくとしてAzure Media OCRメディアプロセッサ利用の注意点として次の2つがある:

    • Azure Media OCR Simplified Output」の記事でアナウンスされている通り2016年9月ごろからデフォルトの出力形式が上記ドキュメントページにある詳細形式からシンプル形式に変わっている
    • Azure Media OCRメディアプロセッサー(MP)は現時点(2016年11月7日)では正式リリースではなくプレビューリリース

    字幕(Closed Caption)データフォーマットへの変換

    まず上記Azure Media OCRメディアプロセッサー(MP)から出力されたJSONファイルの内容を元に字幕用のデータフォーマットであるWebVTTフォーマットファイルを生成している。そして「Azure Media & Cognitiveデモ:Speech-To-Text」でも紹介したようにHTML5のtrackタグエレメントによるビデオファイルの字幕表示機能使ってOCRの内容の字幕表示を実現している。本デモではHTML5に下記のように動画(TransferanAzuresubscriptionJP.mp4)をVideoソースとしてtrackエレメントにWebVTTファイル(azuresubs.vtt)を指定している。

    <video id="Video1" controls autoplay width="600">
        <source src="TransferanAzuresubscriptionJP.mp4" srclang="en" type="video/mp4">
        <track id="trackJA"  src="azuresubs.vtt"  kind="captions" srclang="ja" label="OCR Subtitle" default>
    </video>
    

    Azure Searchによる全文検索

    デモページ上部にある検索窓にキーワードを入力してGoボタンを押すとビデオコンテンツからOCR抽出されたテキストを元に生成された字幕データを全文検索してキーワードにマッチしたテキストとその表示時間に絞り込むことができる。仕組みは「Azure Media & Cognitiveデモ:Speech-To-Text」と全く同じで、Azure Searchを使用して字幕データを解析して字幕表示時間とその対応テキストを1ドキュメントレコードとしてAzure Searchにインジェストしてその生成されたインデックスに対してキーワードを元に全文検索することで実現している。検索用のインデックススキーマもまったくおなじで次のように字幕表示時間とその対応テキストをレコード単位となるように定義している。

    {
        "name": "ocr",
        "fields": [
            { "name":"id", "type":"Edm.String", "key": true, "searchable": false, "filterable":false, "facetable":false },
            { "name":"contentid", "type":"Edm.String","searchable": false, "filterable":true, "facetable":false },
            { "name":"beginsec", "type":"Edm.Int32", "searchable": false, "filterable":false, "sortable":true, "facetable":false },
            { "name":"begin", "type":"Edm.String", "searchable": false, "filterable":false, "sortable":false, "facetable":false },
            { "name":"end", "type":"Edm.String", "searchable": false, "filterable":false, "sortable":false, "facetable":false },
            { "name":"caption", "type":"Edm.String", "searchable": true, "filterable":false, "sortable":false, "facetable":false, "analyzer":"ja.microsoft" }
         ]
    }
    

    デモデータ作成手順

    GithubプロジェクトページVideo-OCR-Search-Pythonの1. Preparationと2. Batch executionを実施いただければOCR抽出されたテキストを元に字幕データ*.vttファイルが生成され、そのテキストがAzure Searchに格納されてデモページ表示のための準備は完了する。最後に表示用のページを生成すれば完了。本デモの表示用ページデータはこちらで、基本的にindex.htmlとsearch.jsの変更のみでいけるはず。

    本デモコンテンツについて何か問題を発見した場合はこちらのGithub IssueページにIssueとして登録いただけると幸いである。

    Enjoy Video OCR demo!

  • Speech-To-Text with Azure Media & Cognitive Services

    ビデオコンテンツを音声認識エンジンでテキスト化してそれを元にスピーチ検索するデモコンテンツを紹介したい。これは過去にde:code2016というマイクロソフトの開発者向けイベントで行ったブレイクアウトセッション「DEV-18: Azure Search Deep Dive」にて紹介したビデオコンテンツのスピーチ検索デモを簡略化して再利用しやすいものにしたものである。

    Video STT Demo Screenshot

    主要テクノロジーと機能

    Azure Media Indexer 2 Previewによる音声からテキスト抽出

    このデモではAzure Media Indexer 2 Preview メディア プロセッサー (MP)を使用してビデオコンテンツからテキストを抽出している。このAzure Media Indexer 2 Previewは自然言語処理(NLP)や音声認識エンジンを駆使してビデオコンテンツより字幕用データ(時間やテキスト)や検索可能にするためのメタデータを抽出することができる。Indexer 2という名前の通り前のバージョンであるAzure Media Indexerが存在するが、これと比較すると、Azure Media Indexer 2 Previewは、インデックス作成が高速化され、より多くの言語をサポートしていることが特徴である。2016年11月6日時点で英語、スペイン語、フランス語、ドイツ語、イタリア語、中国語、ポルトガル語、アラビア語などがサポートされている(残念ながら日本語はまだ未サポート)。

    下イメージはAzure Media Indexer 2 (Preview)で生成されるTTMLWebVTTという代表的な字幕データフォーマット。

    AzureMediaIndexer-ClosedCaption

    HTML5と字幕(Closed Caption)

    HTML5にはtrackタグエレメントを使ってビデオファイルに字幕を表示する機能が標準的に実装されている。本デモではHTML5に下記のように動画(Python_and_node.js_on_Visual_Studio.mp4)をVideoソースとしてtrackエレメントに字幕WebVttファイル(build2016breakout.vtt)を指定している。

    <video id="Video1" controls autoplay width="600">
        <source src="Python_and_node.js_on_Visual_Studio.mp4" srclang="en" type="video/mp4">
        <track id="trackJA"  src="build2016breakout.vtt"  kind="captions" srclang="ja" label="Closed Captions" default>
    </video>
    

    Azure Searchによる全文検索

    デモページ上部にある検索窓にキーワードを入力してGoボタンを押すとビデオコンテンツの字幕データを全文検索してキーワードにマッチしたテキストとその表示時間に絞り込むことができる。ここでは全文検索エンジンにAzure Searchを使用し、Azure Media Indexer 2 (Preview)より抽出された字幕データを解析して字幕表示時間とその対応テキストを1ドキュメントレコードとしてAzure Searchにインジェストしてその生成されたインデックスに対してキーワードを元に全文検索することで実現している。字幕データ検索用のインデックススキーマは次のように字幕表示時間とその対応テキストをレコード単位となるように定義している。

    {
        "name": "stt",
        "fields": [
            { "name":"id", "type":"Edm.String", "key": true, "searchable": false, "filterable":false, "facetable":false },
            { "name":"contentid", "type":"Edm.String","searchable": false, "filterable":true, "facetable":false },
            { "name":"beginsec", "type":"Edm.Int32", "searchable": false, "filterable":false, "sortable":true, "facetable":false },
            { "name":"begin", "type":"Edm.String", "searchable": false, "filterable":false, "sortable":false, "facetable":false },
            { "name":"end", "type":"Edm.String", "searchable": false, "filterable":false, "sortable":false, "facetable":false },
            { "name":"caption", "type":"Edm.String", "searchable": true, "filterable":false, "sortable":false, "facetable":false, "analyzer":"en.microsoft" }
         ]
    }
    

    デモデータ作成手順

    GithubプロジェクトページVideo-STT-Search-Pythonの1. Preparationと2. Batch executionを実施いただければ字幕データ*.vttファイルが生成され、そのテキストがAzure Searchに格納されてデモページ表示のための準備は完了する。最後に表示用のページを生成すれば完了。本デモの表示用ページデータはこちらで、基本的にindex.htmlとsearch.jsの変更のみでいけるはず。

    本デモコンテンツについて何か問題を発見した場合はこちらのGithub IssueページにIssueとして登録いただけると幸いである。

    Enjoy Video Speech-to-text demo!

  • Making SSH lives in Azure easier with azuresshconfig

    UPDATED 2016-10-31: paramsオプション + Bash Completion追加

    みんな大好きSSHとAzureのお話し。物理サーバ、EC2/仮想マシン、コンテナなどなんでもよいがその上にLinuxサーバをたてたらまずやることの1つにSSHログインのためにそのIPアドレス調べて~/.ssh/configにそのエントリーを追加してやることがあるんじゃないかと思います。この作業、エントリー数が少なければ大したことはないものの、追加対象のホストが大量にある場合はかなり面倒な作業になってきます。さらにDHCPなどでアドレスを動的に取得するような設定であればサーバの上げ下げのたびにIPアドレスが変わってくるので~/.ssh/configの更新が必要になってきて、どうしようもなく面倒になってきます。こういった単純でどうしようもなくつまらない作業は自動化したいですよね? ここではそんな皆さんのためにazuresshconfigというツールを紹介させていただきます。

    これは皆さんのAzureサブスクリプション下に作られた仮想マシン一覧(ARMに限る)の情報を取得して各仮想マシンごとのエントリー情報(マシン名とIPアドレス)を~/.ssh/configに追加・更新してくれるツール。新規に仮想マシンを追加した際や、仮想マシンのIPアドレスが追加した際にはazuresshconfigを実行してあげることで~/.ssh/configが最新のエントリー情報でアップデートされ、各マシンにマシン名でSSHログインできるようになります。

    ちなみに、~/.ssh/configとは何ですか?という人はQiitaの記事「~/.ssh/configについて」がとても分かりやすく書かれているので参考になるかと。

    インストール

    Pythonパッケージ管理ツールpipを使ってazuresshconfigをインストールしてください。インストール時に何かエラーが発生した場合は、こちらのページを参照いただき特に該当する事象がないか確認ください。

    $ pip install azuresshconfig
    

    設定ファイルの編集(サービスプリンシパル)

    $ vi $HOME/.azure/azuresshconfig.json
    
    {
        "subscription_id": "",
        "client_id": "",
        "client_scret": "",
        "tenant_id": ""
    }
    

    サービスプリンシパルを作る必要があります。サービスプリンシパルの作り方が分からない人、とってもよいドキュメントがあります。こちらを参照ください:「Use Azure CLI to create a service principal to access resources

    使い方

    azuresshconfig --help
    
    usage: azuresshconfig.py [-h] [--version] [--init] [--profile PROFILE]
                             [--user USER] [--identityfile IDENTITYFILE]
                             [--private] [--resourcegroups RESOURCEGROUPS]
                             [--params PARAMS]
    
    This program generates SSH config from Azure ARM VM inventry in subscription
    
    optional arguments:
      -h, --help            show this help message and exit
      --version             show program's version number and exit
      --init                Create template client profile at
                            $HOME/.azure/azuresshconfig.json only if there is no
                            existing one
      --profile PROFILE     Specify azure client profile file to use
                            ($HOME/.azure/azuresshconfig.json by default)
      --user USER           SSH username to use for all hosts
      --identityfile IDENTITYFILE
                            SSH identity file to use for all hosts
      --private             Use private IP addresses (Public IP is used by
                            default)
      --resourcegroups RESOURCEGROUPS
                            A comma-separated list of resource group to be
                            considered for ssh-config generation (all resource
                            groups by default)
      --params PARAMS       Any ssh-config params you want to add with query-
                            string format: key1=value1&key2=value2&...
    

    実行する

    1. パラメータ指定なしで実行

    $ azuresshconfig
    

    ~/.ssh/configには下記のように### AZURE-SSH-CONFIG BEGIN ### ~ ### AZURE-SSH-CONFIG END ###のブロック内にマシン名とそのIPアドレス(デフォルト:パブリック)のエントリー一覧が追加・更新されます。

    $ cat ~/.ssh/config
    
    ### AZURE-SSH-CONFIG BEGIN ###
    
    Host myvm1
        HostName 40.74.124.30
    
    Host myvm2
        HostName 40.74.116.134
    ....
    
    ### AZURE-SSH-CONFIG END ###
    

    2. SSHユーザと鍵指定

    $ azuresshconfig --user yoichika --identityfile ~/.ssh/id_rsa
    

    ~/.ssh/configには各エントリーにIPアドレスに加えてユーザ名と鍵のパス情報が追加されます。

    $ cat ~/.ssh/config
    
    ### AZURE-SSH-CONFIG BEGIN ###
    
    Host myvm1
        HostName 40.74.124.30
        IdentityFile /home/yoichika/.ssh/id_rsa
        User yoichika
    
    Host myvm2
        HostName 40.74.116.134
        IdentityFile /home/yoichika/.ssh/id_rsa
        User yoichika
    ....
    
    ### AZURE-SSH-CONFIG END ###
    

    3. プライベートIPを指定

    $ azuresshconfig --user yoichika --identityfile ~/.ssh/id_rsa --private
    

    privateオプションを付けて実行することで~/.ssh/configの各エントリーにはデフォルトのパブリックIPアドレスではなくてプライベートIPアドレスが追加されます。

    4. リソースグループで絞る

    $ azuresshconfig --user yoichika --identityfile ~/.ssh/id_rsa --resourcegroups mygroup1,mygroup2
    

    resourcegroupsオプションを指定することで指定されたリソースグループに所属する仮想マシンのエントリーのみが~/.ssh/configに追加されます。

    5. 追加ssh-configパラメータの指定

    $ azuresshconfig.py --user yoichika \
            --identityfile ~/.ssh/id_rsa \
            --params "Port=2222&Protocol=2&UserKnownHostsFile=~/.ssh/known_hosts&ForwardAgent=yes"
    

    paramsオプションを指定することでその他指定可能なssh-configパラメータを追加することができます。上記のようにparamsssh-configPort、Protocol、UserKnownHostsFile、ForwardAgentキーと値をセットすることで次のように出力されるssh-configに指定したキーと値がセットされます。

    $ cat ~/.ssh/config
    
    ### AZURE-SSH-CONFIG BEGIN ###
    
    Host myvm1
        HostName 40.74.124.30
        IdentityFile ~/.ssh/id_rsa
        User yoichika
        Port 2222
        Protocol 2
        UserKnownHostsFile ~/.ssh/known_hosts
        ForwardAgent yes
    
    Host myvm2
        HostName 40.74.116.134
        IdentityFile /home/yoichika/.ssh/id_rsa
        User yoichika
        Port 2222
        Protocol 2
        UserKnownHostsFile ~/.ssh/known_hosts
        ForwardAgent yes
    ....
    
    ### AZURE-SSH-CONFIG END ###
    

    Shell Completion

    Bashでの補完

    次のようにbash/azuresshconfig_completion.bashをbash起動時に読み込ませてあげることでazuresshconfigのパラメータ補完ができるようになります.

    # copy this under either of following directories
    $ cp azuresshconfig_completion.bash (/etc/bash_completion.d | /usr/local/etc/bash_completion.d | ~/bash_completion.d)`
    
    # or append 'source /path/to/azuresshconfig_completion.bash' to .bashrc like this
    $ echo 'source /path/to/azuresshconfig_completion.bash' >> .bashrc
    

    次のようにtabでazuresshconfigのパラメータ補完を行います。

    $ azuresshconfig -[tab]
    -h                --identityfile    --params          --profile         --user
    --help            --init            --private         --resourcegroups
    
    $ azuresshconfig --i[tab]
    --identityfile  --init
    
    $ azuresshconfig --p[tab]
    --params   --private  --profile
    
    $ azuresshconfig --user [tab]
    $ azuresshconfig --user <ssh_user>
    $ azuresshconfig --user <ssh_user> --identityfile [tab]
    $ azuresshconfig --user <ssh_user> --identityfile <ssh_identity_file>
    

    その他

    インストール時のエラーや実行時のエラーについてはこちらに見つけ次第事象とその対応方法を追加しています。 https://github.com/yokawasa/azure-ssh-config/blob/master/Issues.md

    もしバグを見つけたり、追加機能のリクエストがある場合にこちらにIssue追加ください。頑張って時間をみつけて対応します。 https://github.com/yokawasa/azure-ssh-config/issues

    azuresshconfig makes your SSH life on Azure easy!

  • embulk plugins for Microsoft Azure Services

    Here is a list of embulk plugins that you can leverage to transfer your data between Microsoft Azure Services and various other databases/storages/cloud services.

    Plugin Name Target Azure Services Note
    embulk-output-azure_blob_storage Blob Storage Embulk output plugin that stores files onto Microsoft Azure Blob Storage
    embulk-input-azure_blob_storage Blob Storage Embulk input plugin that reads files stored on Microsoft Azure Blob Storage
    embulk-output-sqlserver SQL Databases, SQL DWH Embulk output plugin that Inserts or updates records to SQL server type of services like SQL DB/SQL DWH
    embulk-input-sqlserver SQL Databases, SQL DWH Embulk input plugin that selects records from SQL type of services like SQL DB/SQL DWH
    embulk-output-documentdb Comos DB Embulk output plugin that dumps records to Azure Cosmos DB
    embulk-output-azuresearch Azure Search Embulk output plugin that dumps records to Azure Search

    (as of Aug 30, 2016)

    For embulk, check this site: https://github.com/embulk/embulk

    embulk-screenshot

  • fluent-plugin-documentdb supports Partitioned collections

    I’d like to announce fluent-plugin-documentdb finally supports Azure DocumentDB Partitioned collections for higher storage and throughput. If you’re not familiar with fluent-plugin-documentdb, read my previous article before move on.

    Partitioned collections is kick-ass feature that I had wanted to support in fluent-plugin-documentdb since the feature came out public (see the announcement). For big fan of fluent-plugin-documentdb, sorry for keeping you waiting for such a long time :-) If I may make excuses, I would say I haven’t had as much time on the project, and I had to do ruby client implementation of Partitioned collections by myself as there is no official DocumentDB Ruby SDK that supports it (As a result I’ve created tiny Ruby DocumentDB client libraries that support the feature. Check this out if you’re interested).

    fluentd-azure-documentdb-collection

    What are Partitioned collections?

    According to official documentation, Partitioned collections can span multiple partitions and support very large amounts of storage and throughput. You must specify a partition key for the collection. Partitioned collections can support larger data volumes and process more requests compared to Single-partitioned collection. Partitioned collections support up to 250 GB of storage and 250,000 request units per second of provisioned throughput[Updated Aug 21, 2016] (@arkramac pointed that out for me) Partitioned collections support unlimited storage and throughput. 250GB storage and 250k req/sec are soft cap. You can increase these limits by contacting and asking Azure support.

    On the other hand, Single-partition collections have lower price options and the ability to query and perform transactions across all collection data. They have the scalability and storage limits of a single partition. You do not have to specify a partition key for these collections.

    Creation of Partitioned collections

    You can create Partitioned collections via the Azure portal, REST API ( >= version 2015-12-16), and client SDKs in .NET, Node.js, Java, and Python. In addition, you let fluent-plugin-documentdb create Partitioned collections automatically by adding the following configuration options upon the ones for single-partitioned collection in fluentd.conf:

    • auto_create_collection true
    • partitioned_collection true
    • partition_key [partition key for the collection]
    • offer_throughput [Offer throughtput value: must be more than and equals to 10100. See this for more info on offer throughtput in creating collection]

    It creates a partitioned collection as you configure in starting the plugin if not exist at that time.

    Configuration Example

    Suppose that you want to read Apache access log as source for fluentd, and that you pick “host” as a partition Key for the collection, you can configure the plugin like this following:

    <source>
        @type tail                          # input plugin
        path /var/log/apache2/access.log   # monitoring file
        pos_file /tmp/fluentd_pos_file     # position file
        format apache                      # format
        tag documentdb.access              # tag
    </source>
    
    <match documentdb.*>
        @type documentdb
        docdb_endpoint https://yoichikademo.documents.azure.com:443/
        docdb_account_key Tl1xykQxnExUisJ+BXwbbaC8NtUqYVE9kUDXCNust5aYBduhui29Xtxz3DLP88PayjtgtnARc1PW+2wlA6jCJw==
        docdb_database mydb
        docdb_collection my-partitioned-collection
        auto_create_database true
        auto_create_collection true
        partitioned_collection true 
        partition_key host
        offer_throughput 10100
        localtime true
        time_format %Y%m%d-%H:%M:%S
        add_time_field true
        time_field_name time
        add_tag_field true
        tag_field_name tag
    </match>
    

    Basically that’s all additional configuration for Partitioned collections. Please refer to my previous article for the rest of setup and running work for the plugin.

    Happy log collections with fluent-plugin-documentdb!!