LFS258 [11/15]: Logging and Troubleshooting
Tags: kubernetes, devops, docker, beginners
Published: 2020-04-19 10:39:15

# TL;DR

- [Basic Troubleshooting Steps](#Basic-Troubleshooting-Steps): ต้องไล่จากจุดที่เห็นชัดที่สุด ที่เล็กที่สุด แล้วจึงค่อยๆ ขยาย scope ขึ้นไปเรื่อยๆ
- [Ephemeral Containers](#Ephemeral-Containers): เป็นวิธีที่จะช่วยให้เรา debug ปัญหาที่ยากต่อการ reproduce ได้สะดวกยิ่งขึ้น โดยการเพิ่ม container เข้าไปยัง Pod ที่ run อยู่ feature นี้ยังเป็น alpha อยู่ อาจเปลี่ยนแปลงได้ตลอดเวลา และ มัน disable โดย default ถ้าจะใช้ต้องไปเปิด feature gates ที่ api-server ก่อน
- [Cluster Start Sequence](#Cluster-Start-Sequence): เริ่มจาก systemd ไป start kubelet และ container engine จากนั้น kubelet start static pod ที่เป็น control plane จากนั้น kube-controller-manager ซึ่งเป็น 1 ใน static pods ทำการอ่านค่าจาก etcd แล้ว create resource ต่างๆ ที่เหลือทั้งหมด
- [Monitoring](#Monitoring): ตอนนี้ใช้ Metric Server เป็นตัวรวบรวม metric ต่างๆ และ provide standard API ให้คนอื่นเรียกใช้ผ่านมัน
- [Plugins](#Plugins): ถ้า command ของ kubectl ไม่เพียงพอกับความต้องการของเรา เราสามารถเขียนเพิ่มเองได้ในรูปของ plugins
- [Managing Plugins](#Managing-Plugins): มี project ชื่อ krew เป็น plugin ของ kubelet ที่ทำหน้าที่เป็น plugin manager คอยจัดการ plugin ต่างๆ ให้ ทั้ง install, upgrade และ uninstall
- [Sniffling Traffic With Wireshark](#Sniffling-Traffic-With-Wireshark): ใช้ plugin หนึ่งของ krew ชื่อ sniff โดยมันจะไป start docker container ที่มี tcpdump มา dump network ของ container เรา
- [Logging Tools](#Logging-Tools): kuebernetes ใช้ fluentd เป็น unified logging layer เพื่อรวบรวม log แล้ว ship ไป ยัง logging backend ต่างๆ
- [More Resources](#More-Resources)


<a name="Basic-Troubleshooting-Steps"></a>

---

# Basic Troubleshooting Steps

ในการ troubleshoot ปัญหาที่เกิดขึ้นใน kubernetes cluster ของเราควรเริ่มต้นจากอะไรที่เห็นชัดเจนและเล็กที่สุดก่อน จากนั้นค่อยๆ ขยายขึ้นมาในระดับ cluster โดย steps ทำงานเป็นได้ดังนี้

1. Error ที่เกิดขึ้นจาก command line
2. Log และ สถานะของ Pods
3. ใช้ shell เพื่อเข้าไป troubleshoot ใน pods หรือ container
4. ตรวจสอบสถานะ และ resources ของ Node
5. RBAC, SELinux หรือ AppArmor สำหรับ security settings
6. ตรวจสอบ API calls ของ kube-apiserver
7. Enable auditing
8. ตรวจสอบ network ระหว่าง node รวมทั้ง DNS และ firewall
9. ตรวจสอบ master server controller ว่า run ครบหรื่อมี error อะไรเกิดขึ้นหรือไม่

<a name="Ephemeral-Containers"></a>

---

# Ephemeral Containers

ตั้งแต่ v1.16 เป็นต้นมา เราสามารถเพิ่ม container เข้าไปยัง Pod ที่ run อยู่ได้ โดยที่ไม่ต้อง re-create pod ใหม่ ด้วยความสามารถนี้ช่วยให้ investigate ปัญหาที่ยากต่อการ reproduce ได้สะดวกยิ่งขึ้น container ทีเพิ่มเข้าไปใน Pods ที่ run อยู่เรียกว่า "`ephemeral containers`"

> feature นี้ยังคงเป็น alpha อยู่ อาจเปลี่ยนแปลงได้ 

ด้วยความที่มันถูกเพิ่มเข้าไปใน Pod run อยู่แล้ว ทำให้มันไม่มีความสามารถบางเหมือน container ทั่วไป เช่น

- ไม่มี ports ดังนั้นจึงระบุ  `ports`, `livenessProbe` และ `readinessProbe` ไม่ได้
- เนื่องจาก Pod resource มีคุณสมบัติ immutable (เปลี่ยนแปลงไม่ได้) ดังนั้น ephemeral containers ใช้ `resources` ไม่ได้

สามารถดูคุณสมบัติเพิ่มเติมได้จาก [EphemeralContainer reference documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#ephemeralcontainer-v1-core)

การเพิ่ม ephemeral containers ไม่สามารถทำผ่าน `pod.spec` โดยใช้ `kubectl edit` ได้ 

> **ephemeral containers** ถูก disable โดย default ดังนั้นต้องทำการ enable ก่อนโดยเพิ่ม `--feature-gates=EphemeralContainers=true` ใน options ของ api-server ใน `/etc/kubernetes/manifests/ kube-apiserver.yaml`

การใช้งาน Ephemeral Container ก่อน v1.18 ต้องสั่งงานผ่าน API ดังนี้

``` bash
# สร้าง pod ตัวอย่างที่ไม่มี shell 

$ kubectl run ephemeral-demo --image=k8s.gcr.io/pause:3.1 --restart=Never
pod/ephemeral-demo created

# เพิ่ม ephemeral container เข้าไปใน pod

$ cat > ec.json << EOF
{
    "apiVersion": "v1",
    "kind": "EphemeralContainers",
    "metadata": {
            "name": "ephemeral-demo"
    },
    "ephemeralContainers": [{
        "command": [
            "sh"
        ],
        "image": "busybox",
        "imagePullPolicy": "IfNotPresent",
        "name": "debugger",
        "stdin": true,
        "tty": true,
        "terminationMessagePolicy": "File"
    }]
}
EOF

$ kubectl replace --raw /api/v1/namespaces/default/pods/ephemeral-demo/ephemeralcontainers -f ec.json
$ kubectl describe pod ephemeral-demo
(...)
Ephemeral Containers:
debugger:
    Image:      busybox
    Port:       <none>
    Host Port:  <none>
    Command:
    sh
    Environment:  <none>
    Mounts:       <none>
(...)

# Attach ไปยัง ephemeral container เพื่อทำการ debug

$ kubectl attach -it example-pod -c debugger
```

ถ้าใน v1.18 สามารถ run `kubectl alpha debug -it ...` ได้เลย

``` bash
# สร้าง pod ตัวอย่างที่ไม่มี shell 

$ kubectl run ephemeral-demo --image=k8s.gcr.io/pause:3.1 --restart=Never
pod/ephemeral-demo created

# เพิ่ม ephemeral container เข้าไปใน pod เพื่อทำการ debug

$ kubectl alpha debug -it ephemeral-demo --image=busybox --target=ephemeral-demo
```

<a name="Cluster-Start-Sequence"></a>

---

# Cluster Start Sequence

ถ้าเรา setup cluster ของเราด้วย `kubeadm` หรือ tool automation อื่นๆ ที่ใช้ `kubeadm` ลำดับการ startup ของ cluster เราจะเริ่มต้นดังนี้

1. `systemd` ของแต่ละเครื่องใน cluster จะทำการ start **kubelet** ขึ้นมาก่อน ซึ่งเราสามารถตรวจสอบ parameter ต่างๆ ในการ start `kubelet` ได้จาก configuration file ของมันที่ `{/etc,/usr/lib/systemd}/system/kubelet.service.d/10-kubeadm.conf`
2. `kubelet` จะทำการ start **Static Pod** โดยเข้าไปอ่าน file ที่ระบุมาใน parameter `--config=` ซึ่ง default จะเป็น `/var/lib/kubelet/config.yaml` จากนั้น หาตัวแปร `staticPodPath` ซึ่งเป็นตัวที่ระบุ path ที่เก็บ configuration file ของแต่ละ static pod ซึ่ง โดย default จะชี้ไปที่ `/etc/kubernetes/manifests/` จากนั้นจึงทำการ start static pod ตาม configuration file โดยปกติจะมี 4 control plane pods นี้
    - `etcd`
    - `kube-controller-manager`
    - `kube-scheduler`
    - `kube-apiserver`
3. `kube-controller-manager` ที่ทำหน้าที่เป็น watch loop และ controllers จะอ่าน data จาก `etcd` เพื่อทำการสร้าง resources ที่เหลือทั้งหมด

![Cluster Start Sequence](https://dev-to-uploads.s3.amazonaws.com/i/l8wmakbhaqt25l44pej5.png)

<a name="Monitoring"></a>

---

# Monitoring

การ monitoring ทุกคนคงรู้จักกันดีอยู่แล้วว่ามันคือการไปเอาค่า (metric) ต่างๆ จาก infrastructure และ application เพื่อนำมาตรวจวัดและวิเตราะห์ เพื่อต่อยอดในการทำงานอื่นๆ ต่อไป 

ใน kubernetes เมื่อก่อนจะใช้ **Heapster** ในการรวบรวม metrics ตาม components ต่างๆ ใน cluster แต่ตอนนี้ **Heapster** ได้ deplicated ไปแล้ว และมีสิ่งที่มาแทนเรียกว่า **Metrics Server** โดย **Metric Server** ทำหน้าที่ตัวรวบรวม metrics เช่นเดิม เพิ่มเติมคือมี standard API ซึ่งจะให้ agent ต่างๆ เข้ามาดึงข้อมูลไปทำงานต่อ เช่น การทำ autocalers (เพิ่มจำนวน replica ตาม workload) เป็นต้น

ในส่วนของ data store และ visualization ของที่คู่กับ **Heapster** ก็คือ **cAdvisor** เมื่อ **Heapster** ไม่อยู่แล้วจึงมี tool ตัวอื่นขึ้นมมาแทนนั่นคือ **Prometheus** ซึ่งเป็น application ที่อยู่ใน [CNCF](https://www.cncf.io/) เช่นเดียวกับ Kubernetes โดย **Prometheus** ทำหน้าที่เป็น plugin ของ kubernetes ที่ใช้ในการดึงค่าของ resources ต่างๆ ใน cluster ผ่าน **Metric Server** นอกจากนั้นมันยังมี library ในภาษาต่างๆ เพื่อ integrate และ ดึง metric ในระดับ application ด้วย

<a name="Plugins"></a>

---

# Plugins

ถ้า `kubectl` ไม่พอความต้องการของเรา เราสามารถเขียน sub-command เพิ่มได้เอง เพียงแค่ตั้งชื่อให้ขึ้นต้นว่า `kubectl-` เช่น `kubectl-sniff` แล้ววางไว้ใน directory ไหนก็ได้ที่ระบุไว้ใน environment variable ชื่อ `PATH` จากนั้นเปลี่ยน mode ให้ execute ได้ เราก้ได้ sub-comand ใหม่ขึ้นมาแล้ว

ตัวอย่างการสร้าง plugin ขึ้นมาใช้เอง

``` bash
# สร้าง plugin และ register ให้ kubectl เรียกได้

$ cat > kubectl-foo << EOF
#!/bin/bash

# optional argument handling
if [[ "\$1" == "version" ]]
then
    echo "1.0.0"
    exit 0
fi

# optional argument handling
if [[ "\$1" == "config" ]]
then
    echo "\$KUBECONFIG"
    exit 0
fi

echo "I am a plugin named kubectl-foo"
EOF
$ chmod +x ./kubectl-foo
$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/admin/.local/bin:/home/admin/bin
$ sudo mv ./kubectl-foo /usr/local/bin

# List plugins

$ kubectl plugin list
The following compatible plugins are available:

/usr/local/bin/kubectl-foo

# ทดลองใช้งาน

$ kubectl foo
I am a plugin named kubectl-foo
$ kubectl foo version
1.0.0

# จะเห็นว่า kubectl pass environment variables ทุกอย่างมาให้ script เราด้วย

$ export KUBECONFIG=~/.kube/config
$ kubectl foo config
/home/admin/.kube/config
$ KUBECONFIG=/etc/kube/config kubectl foo config
/etc/kube/config
```

<u>**ข้อควรรู้**</u>

1. ถ้าเราตั้งชื่อ plugin เป็น `kubectl-foo-bar-baz` เราจะใช้ได้แบบ `kubectl foo bar baz`

    ``` bash
    $ echo -e '#!/bin/bash\n\necho "My first command-line argument was $1"' > kubectl-foo-bar-baz
    $ sudo chmod +x ./kubectl-foo-bar-baz
    $ sudo mv ./kubectl-foo-bar-baz /usr/local/bin

    $ kubectl plugin list
    The following compatible plugins are available:

    /usr/local/bin/kubectl-foo
    /usr/local/bin/kubectl-foo-bar-baz

    $ kubectl foo bar baz arg1 --meaningless-flag=true
    My first command-line argument was arg1
    ```

2. ในการเรียกใช้ plugin จะใช้ concept best match

    ``` bash
    $ echo -e '#!/bin/bash\n\necho "My fizz buzz command-line argument was $1"' > kubectl-fizz-buzz
    $ echo -e '#!/bin/bash\n\necho "My fizz command-line argument was $1"' > kubectl-fizz
    $ sudo chmod +x ./kubectl-fizz-buzz
    $ sudo chmod +x ./kubectl-fizz
    $ sudo mv ./kubectl-fizz-buzz /usr/local/bin
    $ sudo mv ./kubectl-fizz /usr/local/bin

    $ kubectl plugin list
    The following compatible plugins are available:

    /usr/local/bin/kubectl-fizz
    /usr/local/bin/kubectl-fizz-buzz

    $ kubectl fizz buzz arg1
    My fizz buzz command-line argument was arg1
    $ kubectl fizz arg1
    My fizz command-line argument was arg1
    ```

3. ถ้าตั้งชื่อมี `underscore (_)` เวลาเรียกใช้สามารถใช้ได้ทั้ง `underscore (_)` และ `dash(-)`

    ``` bash
    $ echo -e '#!/bin/bash\n\necho "I am a plugin with a dash in my name"' > ./kubectl-foo_bar
    $ sudo chmod +x ./kubectl-foo_bar
    $ sudo mv ./kubectl-foo_bar /usr/local/bin

    $ kubectl plugin list
    The following compatible plugins are available:

    /usr/local/bin/kubectl-foo_bar

    $ kubectl foo_bar
    I am a plugin with a dash in my name
    $ kubectl foo-bar
    I am a plugin with a dash in my name
    ```

4. ถ้ามี file ชื่อซ้ำกันในแต่ละ directory ของ `PATH` file ที่อยู่ใน directory แรกจะถูกเรียกใช้งาน (overshadow)

    ``` bash
    $ echo $PATH
    /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin

    $ echo -e '#!/bin/bash\n\necho "My first command-line argument was $1"' | sudo tee /usr/local/bin/kubectl-overshadow
    $ echo -e '#!/bin/bash\n\necho "My second command-line argument was $1"' | sudo tee  /usr/sbin/kubectl-overshadow
    $ sudo chmod +x /usr/local/bin/kubectl-overshadow
    $ sudo chmod +x /usr/sbin/kubectl-overshadow

    $ kubectl plugin list
    The following compatible plugins are available:

    /usr/local/bin/kubectl-overshadow
    /usr/sbin/kubectl-overshadow
    - warning: /usr/sbin/kubectl-overshadow is overshadowed by a similarly named plugin: /usr/local/bin/kubectl-overshadow

    error: one plugin warning was found

    $ kubectl overshadow 1234
    My first command-line argument was 1234
    ```

5. ไม่สามารถ override built-in sub-command ของ kubelet ได้

    ``` bash
    $ echo -e '#!/bin/bash\n\necho "My version is 1.0.0"' | sudo tee /usr/local/bin/kubectl-version
    $ sudo chmod +x /usr/local/bin/kubectl-version

    $ kubectl plugin list
    The following compatible plugins are available:

    /usr/local/bin/kubectl-version
    - warning: kubectl-version overwrites existing command: "kubectl version"

    error: one plugin warning was found

    $ kubectl version
    Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.2", GitCommit:"52c56ce7a8272c798dbc29846288d7cd9fbae032", GitTreeState:"clean", BuildDate:"2020-04-16T11:56:40Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
    Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.2", GitCommit:"52c56ce7a8272c798dbc29846288d7cd9fbae032", GitTreeState:"clean", BuildDate:"2020-04-16T11:48:36Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
    ```

<a name="Managing-Plugins"></a>

---

# Managing Plugins

ถ้าหากคุณ develop plugin ออกมาได้ดี และ อยากจะ distribute ให้คนอื่นใช้ด้วย มี project ที่ชื่อว่า [krew](https://krew.sigs.k8s.io/) ซึ่งทำหน้าที่เป็น plugin manager ของ `kubectl` อ่านเพิ่มเติมการ upload plugin ของตัวเองได้จาก [Developer Guide](https://krew.sigs.k8s.io/docs/developer-guide/)

ในส่วนนี้จะขอแสดงเพียงติดตั้งและใช้งานบน CentOS

1. [Install and setup krew](https://krew.sigs.k8s.io/docs/user-guide/setup/install/)

    ``` bash
    # Installation
    $ sudo yum install git
    $ (
    set -x; cd "$(mktemp -d)" &&
    curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/krew.{tar.gz,yaml}" &&
    tar zxvf krew.tar.gz &&
    KREW=./krew-"$(uname | tr '[:upper:]' '[:lower:]')_amd64" &&
    "$KREW" install --manifest=krew.yaml --archive=krew.tar.gz &&
    "$KREW" update
    )
    $ cat >> ~/.bashrc << EOF
    export PATH="\${KREW_ROOT:-\$HOME/.krew}/bin:\$PATH"
    EOF
    $ source ~/.bashrc

    # Verification
    $ kubectl krew version
    OPTION            VALUE
    GitTag            v0.3.4
    GitCommit         324f5ed
    IndexURI          https://github.com/kubernetes-sigs/krew-index.git
    BasePath          /home/admin/.krew
    IndexPath         /home/admin/.krew/index
    InstallPath       /home/admin/.krew/store
    BinPath           /home/admin/.krew/bin
    DetectedPlatform  linux/amd64
    ```
2. [วิธีการใช้งาน](https://krew.sigs.k8s.io/docs/user-guide/quickstart/)

    ``` bash
    # Download the plugin list
    $ kubectl krew update
    Updated the local copy of plugin index.

    # Discover plugins available on Krew
    $ kubectl krew search
    NAME                            DESCRIPTION                                         INSTALLED
    access-matrix                   Show an RBAC access matrix for server resources     no
    advise-psp                      Suggests PodSecurityPolicies for cluster.           no
    apparmor-manager                Manage AppArmor profiles for cluster.               no
    auth-proxy                      Authentication proxy to a pod or service            no
    bulk-action                     Do bulk actions on Kubernetes resources.            no
    (...)

    # Choose a plugin from the list and install it
    $ kubectl krew install whoami
    Updated the local copy of plugin index.
    Installing plugin: whoami
    Installed plugin: whoami
    (...)

    # Use the installed plugin
    $ kubectl whoami
    kubecfg:certauth:admin

    # List existing plugins
    $ kubectl plugin list
    The following compatible plugins are available:

    /home/admin/.krew/bin/kubectl-krew
    /home/admin/.krew/bin/kubectl-whoami

    # Keep your plugins up-to-date
    $ kubectl krew upgrade
    Updated the local copy of plugin index.
    Upgrading plugin: krew
    Skipping plugin krew, it is already on the newest version
    Upgrading plugin: whoami
    Skipping plugin whoami, it is already on the newest version

    # Uninstall a plugin you no longer use
    $ kubectl krew uninstall whoami
    Uninstalled plugin whoami
    ```

<a name="Sniffling-Traffic-With-Wireshark"></a>

---

# Sniffling Traffic With Wireshark

จากหัวข้อที่แล้วเรารู้จัก knew กันไปแล้ว คราวนี้เรามาใช้ plugin ของมันที่ชื่อว่า sniff เพื่อทำการ tcpdump network ภายใน container ของเรากัน โดยสามารถอ่านรายละเอียดได้จาก [github ของ eldadru](https://github.com/eldadru/ksniff)

``` bash
$ sudo yum install wireshark
$ kubectl krew install sniff
Updated the local copy of plugin index.
Installing plugin: sniff
Installed plugin: sniff
(...)

$ kubectl run demo --image=nginx  --restart=Never
pod/demo created

$ kubectl sniff -p demo -c demo -o - | tshark -r -
INFO[0000] sniffing method: privileged pod              
INFO[0000] using tcpdump path at: '/home/admin/.krew/store/sniff/v1.4.1/static-tcpdump' 
INFO[0000] sniffing on pod: 'demo' [namespace: 'default', container: 'demo', filter: '', interface: 'any'] 
INFO[0000] creating privileged pod on node: 'kube-0002.novalocal' 
INFO[0000] pod created: &Pod{ObjectMeta:{ksniff-k9vxv ksniff- default
(...)
 45         37 192.168.93.128 -> 192.168.52.75 TCP 76 40910 > http [SYN] Seq=0 Win=28000 Len=0 MSS=1400 SACK_PERM=1 TSval=3831288949 TSecr=0 WS=128
 46         37 192.168.52.75 -> 192.168.93.128 TCP 76 http > 40910 [SYN, ACK] Seq=0 Ack=1 Win=27760 Len=0 MSS=1400 SACK_PERM=1 TSval=3831285944 TSecr=3831288949 WS=128
 47         37 192.168.93.128 -> 192.168.52.75 TCP 68 40910 > http [ACK] Seq=1 Ack=1 Win=28032 Len=0 TSval=3831288949 TSecr=3831285944
 48         37 192.168.93.128 -> 192.168.52.75 HTTP 145 GET / HTTP/1.1 
 49         37 192.168.52.75 -> 192.168.93.128 TCP 68 http > 40910 [ACK] Seq=1 Ack=78 Win=27776 Len=0 TSval=3831285945 TSecr=3831288950
 50         37 192.168.52.75 -> 192.168.93.128 TCP 307 [TCP segment of a reassembled PDU]
 51         37 192.168.52.75 -> 192.168.93.128 HTTP 680 HTTP/1.1 200 OK  (text/html)
(...)
```

จะเห็นว่า หลักการของมันคือการ ไปสร้าง docker container `corfr/tcpdump` ที่ attach เข้า network เดียวกับ pod เรา แล้วทำการ tcpdump

<a name="Logging-Tools"></a>

---

# Logging Tools

เรื่อง Log ถือเป็นเรื่องใหญ่มากในวงการ IT หลักการโดยทั่วไปของคือ ทำการ ingest log เข้ามาเก็บไว้ที่ seach engine แล้วทำการ query ด้วย search syntax เพื่อแสดงผลผ่าน dashboard ซึ่ง application ที่ได้รับความนิยมคงหนีไม่พ้น Elastic Stack ซึ่งประกอบไปด้วย Elasticsearch, Logstash และ Kibana

ใน kubernetes, `kubelet` จะทำการ write container log ลง local file ผ่าน Docker logging driver จึงมักใช้ `Fluentd` หรือ `Fluent Bit` ในการ รวม log แล้วจึงส่งออกไปยัง log server ไม่ว่าจะเป็น Elasticsearch หรือ Prometheus

Logging Architecture ของ kubernetes มีดังนี้ 

1. Logging at the node level

    ![Logging at the node level](https://dev-to-uploads.s3.amazonaws.com/i/wcqojynvftzj6ocsdm1v.png)

    ไม่มีการ ship ออกไปยัง logging backend ทุกอย่างอยู่บนเครื่องใช้ log rotate engine ของ OS นั้นๆ ในการ rotate log ถ้าใน linux ก็จะเป็น [logrotate](https://linux.die.net/man/8/logrotate)

2. Cluster-level logging architectures

    2.1 Using a node logging agent

    ![Cluster-level logging architectures](https://dev-to-uploads.s3.amazonaws.com/i/z65n8xfvdl6a4zx0mpyc.png)

    container ทำการเขียน log ออกทาง `stdout` และ `stderr` จากนั้น log driver ของ container runtime จะทำการ write log ลง local file จากนั้น เราจึงใช้ logging agent เช่น `fluentd` ในการ ship log ออกไปยัง logging backend อย่างเช่น Elasticsearch หรือ Stackdriver Logging

    2.2 Using a sidecar container with the logging agent

    **Streaming sidecar container**

    ![Streaming sidecar container](https://dev-to-uploads.s3.amazonaws.com/i/xaqtlqrrqe8em9vvxgb1.png)

    ในกรณีที่ application container ไม่สามารถ write log ไปยัง `stdout` และ `stderr` ได้ จึงต้องมี sidecar ซึ่งเป็น container ที่อยู่ใน Pod เดียวกับ application container เพื่อทำการอ่าน file, socket หรือ journald เพื่อ redirect มายัง `stdout` และ `stderr` เพื่อให้ log driver ของ container runtime จะทำการ write log ลง local file และ logging agent ทำการอ่าน file นั้นแล้ว ship ไปยัง logging backend ต่อไป

    **Sidecar container with a logging agent**

    ![Sidecar container with a logging agent](https://dev-to-uploads.s3.amazonaws.com/i/njzymot4kil7wemub7os.png)

    ถ้าคุณไม่สะดวกใช้ node-level logging agent ในการ ship log คุณสามารถ install sidecar ที่ทำหน้าที่เป็น logging agent คู่กับ application container เพื่อ ship log ตรงไปยัง logging backend เลยก็ได้

    **Exposing logs directly from the application**

    ![Exposing logs directly from the application](https://dev-to-uploads.s3.amazonaws.com/i/t2n1r2467zgrctflvinl.png)

    วิธีสุดท้ายถ้าคุณสามารถปรับ application ให้ ship log ไปยัง logging agent เองได้เลย วิธีนี้ก็สามารถทำได้เช่นกัน

<a name="More-Resources"></a>

---

# More Resources

- [General Guidelines and instructions](https://kubernetes.io/docs/tasks/debug-application-cluster/troubleshooting/)
- [Troubleshoot Applications](https://kubernetes.io/docs/tasks/debug-application-cluster/debug-application/)
- [Troubleshoot Clusters](https://kubernetes.io/docs/tasks/debug-application-cluster/debug-cluster/)
- [Debug Pods and ReplicationControllers](https://kubernetes.io/docs/tasks/debug-application-cluster/debug-pod-replication-controller/)
- [Debug Services](https://kubernetes.io/docs/tasks/debug-application-cluster/debug-service/)
- [Kubernetes Issues and Bug Tracking](https://github.com/kubernetes/kubernetes/issues)
- [Slack Channel](https://kubernetes.slack.com/)