Работа с Pod

Для практики с подами нам также понадобится установленный kind и утилита kubectl, для удобства можно стереть все данные от предыдущей работы и пересоздать кластер командами:

$ kind delete cluster
$ kind create cluster

Либо можно удалить все оставшиеся поды в неймспейсе командой:

$ k delete pod --all
pod "nginx" deleted

Примечание

Для удобства и компактности для команды kubectl будет использоваться псевдоним k. Как его сделать описано в предыдущем практическом занятии.

Так как большинство полей в поде неизменяемые, то в этом занятии будет использоваться команда kubectl create для создания пода, а вместо изменения под будет удаляться командой kubectl delete.

Создание

Создать под можно командой kubectl create -f -, которая будет ожидать ввода из потока стандартного ввода вместо файла, таким образом можно просто вставить текст в терминал, перевести на новую строку и нажать Ctrl+d. Также можно воспользоваться синтаксисом heredoc в шеле, чтобы передать на стандартный ввод текст между разделителями, в данном случае EOF:

$ k create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
EOF
pod/nginx created

Получение информации

Получить подробную информацию о поде в удобном формате можно командой kubectl describe pod:

$ kubectl describe po nginx
Name:             nginx
Namespace:        default
Priority:         0
Service Account:  default
Node:             kind-worker/172.18.0.3
Start Time:       Mon, 06 Mar 2023 21:02:20 +0300
Labels:           run=nginx
Annotations:      <none>
Status:           Running
IP:               10.244.2.5
IPs:
  IP:  10.244.2.5
Containers:
  nginx:
    Container ID:   containerd://883f111c6fb037bc1991cf2bde34a91e4d3e8238e93aba1d54415243bd4f28c0
    Image:          nginx
    Image ID:       docker.io/library/nginx@sha256:aa0afebbb3cfa473099a62c4b32e9b3fb73ed23f2a75a65ce1d4b4f55a5c2ef2
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Mon, 06 Mar 2023 21:02:22 +0300
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-7jkrp (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  kube-api-access-7jkrp:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  27m   default-scheduler  Successfully assigned default/nginx to kind-worker
  Normal  Pulling    27m   kubelet            Pulling image "nginx"
  Normal  Pulled     27m   kubelet            Successfully pulled image "nginx" in 1.094512433s
  Normal  Created    27m   kubelet            Created container nginx
  Normal  Started    27m   kubelet            Started container nginx

Также информацию можно получить в различных форматах с помощью опции -o команды kubectl get.

Вывод только имен name:

$ k get po -o name
pod/nginx

Расширенный вывод wide:

$ k get po nginx -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP           NODE          NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          31m   10.244.2.5   kind-worker   <none>           <none>

Формат yaml/json:

$ k get po nginx -o yaml | head -15
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2023-03-06T18:02:20Z"
  labels:
    run: nginx
  name: nginx
  namespace: default
  resourceVersion: "19432"
  uid: ce208149-7f16-4226-b50a-f916854cbca3
spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: nginx

Шаблон jsonpath:

$ k get po nginx -o jsonpath='{.spec.containers[0].image}'
nginx

Шаблон go-template:

$ k get pod nginx -o go-template='{{range .spec.containers}}{{.image}}{{"\n"}}{{end}}'
nginx

Заданный формат столбцов:

$ kubectl get pod nginx -o custom-columns=NAME:.metadata.name
NAME
nginx

Также очень популярная в использовании утилита jq для форматирования вывода в виде json:

$ k get po nginx -o json | jq '.status.conditions[] | select(.type == "ContainersReady") | .status'
"True"

Логи

Получить логи контейнера можно командой kubectl logs.

$ k logs nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2023/03/06 18:02:22 [notice] 1#1: using the "epoll" event method
2023/03/06 18:02:22 [notice] 1#1: nginx/1.23.3
2023/03/06 18:02:22 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2023/03/06 18:02:22 [notice] 1#1: OS: Linux 4.19.130-boot2docker
2023/03/06 18:02:22 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2023/03/06 18:02:22 [notice] 1#1: start worker processes
2023/03/06 18:02:22 [notice] 1#1: start worker process 35
2023/03/06 18:02:22 [notice] 1#1: start worker process 36
2023/03/06 18:02:22 [notice] 1#1: start worker process 37
2023/03/06 18:02:22 [notice] 1#1: start worker process 38
127.0.0.1 - - [06/Mar/2023:18:27:10 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.79.1" "-"

Чтобы вывести только последние несколько строк, можно воспользоваться опцией --tail=<n>, где <n> - количество последних строк.

$ k logs nginx --tail=1
127.0.0.1 - - [06/Mar/2023:18:27:10 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.79.1" "-"

Также можно ограничить вывод по времени опциями --since=<t>, где <t> - относительное время, например 5m - за последние 5 минут. А также опцией --since-time=<f>, где <f> - время в формате RFC3339.

$ k logs nginx --since-time='2023-03-06T18:10:00Z'
127.0.0.1 - - [06/Mar/2023:18:27:10 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.79.1" "-"

Если процесс в контейнере не указывает временные отметки в логах, то можно их добавить опцией --timestamps.

$ k logs nginx --timestamps
2023-03-06T18:02:22.892400062Z /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
2023-03-06T18:02:22.892484132Z /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
2023-03-06T18:02:22.899665882Z /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
2023-03-06T18:02:22.912301591Z 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
2023-03-06T18:02:22.922720728Z 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
2023-03-06T18:02:22.924250668Z /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
2023-03-06T18:02:22.930897003Z /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
2023-03-06T18:02:22.934077876Z /docker-entrypoint.sh: Configuration complete; ready for start up
2023-03-06T18:02:22.940752160Z 2023/03/06 18:02:22 [notice] 1#1: using the "epoll" event method
2023-03-06T18:02:22.940780609Z 2023/03/06 18:02:22 [notice] 1#1: nginx/1.23.3
2023-03-06T18:02:22.940791331Z 2023/03/06 18:02:22 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2023-03-06T18:02:22.940797071Z 2023/03/06 18:02:22 [notice] 1#1: OS: Linux 4.19.130-boot2docker
2023-03-06T18:02:22.940800309Z 2023/03/06 18:02:22 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2023-03-06T18:02:22.941055633Z 2023/03/06 18:02:22 [notice] 1#1: start worker processes
2023-03-06T18:02:22.941072933Z 2023/03/06 18:02:22 [notice] 1#1: start worker process 35
2023-03-06T18:02:22.941076973Z 2023/03/06 18:02:22 [notice] 1#1: start worker process 36
2023-03-06T18:02:22.941466310Z 2023/03/06 18:02:22 [notice] 1#1: start worker process 37
2023-03-06T18:02:22.941796944Z 2023/03/06 18:02:22 [notice] 1#1: start worker process 38
2023-03-06T18:27:10.252486288Z 127.0.0.1 - - [06/Mar/2023:18:27:10 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.79.1" "-"

Создадим под с несколькими контейнерами:

$ k delete pod --all
$ k create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
  - image: busybox
    name: date
    command: [/bin/sh, -c, 'while sleep 10;do date;done']
EOF

Если контейнеров в поде несколько, то можно воспользоваться опцией -c или --container для указания конкретного контейнера. Для получения логов всех контейнеров есть опция --all-containers. Для того, чтобы было понятно какой именно контейнер вывел строку, можно добавить опцию --prefix.

$ k logs nginx -c date
Mon Mar  6 19:34:49 UTC 2023
$ k logs nginx --all-containers --prefix --tail=5
[pod/nginx/date] Mon Mar  6 19:34:49 UTC 2023
[pod/nginx/date] Mon Mar  6 19:35:02 UTC 2023
[pod/nginx/date] Mon Mar  6 19:35:19 UTC 2023
[pod/nginx/nginx] 2023/03/06 19:34:37 [notice] 1#1: start worker processes
[pod/nginx/nginx] 2023/03/06 19:34:37 [notice] 1#1: start worker process 35
[pod/nginx/nginx] 2023/03/06 19:34:37 [notice] 1#1: start worker process 36
[pod/nginx/nginx] 2023/03/06 19:34:37 [notice] 1#1: start worker process 37
[pod/nginx/nginx] 2023/03/06 19:34:37 [notice] 1#1: start worker process 38

Для постоянного отслеживания новых сообщений в логе есть опция -f или --follow.

$ k logs nginx -c date -f
Mon Mar  6 19:34:49 UTC 2023
Mon Mar  6 19:35:02 UTC 2023
Mon Mar  6 19:35:19 UTC 2023
Mon Mar  6 19:35:35 UTC 2023
Mon Mar  6 19:35:49 UTC 2023
Mon Mar  6 19:36:02 UTC 2023

Проброс портов

Есть несколько способов отправлять сетевые запросы внутрь пода, наиболее простой - это проброс портов на localhost командой kubectl port-forward.

$ k port-forward nginx 8080:80 &
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

Команда запускается с символом & в конце для того, чтобы она продолжила работу в фоновом режиме и отдала нам управление терминалом. Из ее вывода видно, что запросы с адреса 127.0.0.1:8080 будут перенаправляться в контейнер на порт 80. Можно использовать команду curl, которая обычно есть по умолчанию, для отправки HTTP запроса в контейнер.

$ curl localhost:8080
Handling connection for 8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Как видно nginx нам ответил своей стандартной страницей приветствия.

Запуск команд в контейнере

Запустить команду в контейнере можно с помощью kubectl exec, если контейнеров несколько, то конкретный можно указать опцией -c или --container.

$ k exec -c nginx nginx -- hostname
nginx

Для запуска в контейнере командной оболочки в интерактивном режиме следует добавить опции -i - для возможности ввода через стандартный ввод и -t для выделения устройства псевдотерминала.

$ k exec -it -c nginx nginx -- /bin/bash
root@nginx:/# pwd
/
root@nginx:/# echo 'hello' > /usr/share/nginx/html/index.html
root@nginx:/# exit
exit
$ curl localhost:8080
Handling connection for 8080
hello

Здесь видно как можно изменить файл index.html изнутри контейнера, который отдает nginx.

Копирование файлов контейнера

Копировать файлы контейнера можно командой kubectl cp, для этого в контейнере должна быть утилита tar. По сути это удобная обертка над командой kubectl exec. Конкретный контейнер также можно указать опцией -c или --container.

$ k cp -c nginx nginx:/usr/share/nginx/html/index.html ./index.html
tar: Removing leading `/' from member names
$ cat ./index.html
hello
$ echo 'world' > ./index.html
$ k cp ./index.html -c nginx nginx:/usr/share/nginx/html/index.html
$ curl localhost:8080
Handling connection for 8080
world

Использование ConfigMap/Secret

Для передачи параметров внутрь контейнера есть удобный способ с использованием ресурсов ConfigMap и Secret. Сами объекты представляют из себя структуру данных в виде ключ-значение. В поде их можно использовать смонтировав в файловую систему как файл или в переменных среды.

$ k create -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: html
data:
  index.html: hello world
EOF
$ k create -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  name: envs
stringData:
  ENV_NAME: ENV_VALUE
EOF
$ k delete pod --all
$ k create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    envFrom:
    - secretRef:
        name: envs
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: html
  volumes:
  - name: html
    configMap:
      name: html
EOF

После пересоздания пода проброс портов нужно сделать заново и можно убедиться, что данные из ConfigMap и Secret попали в под.

$ k port-forward nginx 8080:80 &
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
$ curl localhost:8080
Handling connection for 8080
hello world
$ k exec nginx -- /bin/sh -c 'echo $ENV_NAME'
ENV_VALUE