https://www.altlinux.org/api.php?action=feedcontributions&user=Kaf&feedformat=atomALT Linux Wiki - Вклад [ru]2024-03-28T21:44:10ZВкладMediaWiki 1.38.2https://www.altlinux.org/index.php?title=Rootless_kubernetes&diff=78993Rootless kubernetes2024-03-25T17:22:11Z<p>Kaf: /* Указании версии основных kubernetes образов */</p>
<hr />
<div><br />
<br />
= Общее описание =<br />
<br />
Запуск <code>Kubernetes</code> в режиме <code>rootless</code> обеспечивает запуск <code>Pod</code>ов без системных <code>root</code>-привелегий в рамках <code>user namespace</code> системного пользователя <code>u7s-admin</code>. Работа в этом режиме практически не требует никаких модификаций, но обеспечивает повышенные уровень защищенности <code>kubernetes</code>, так как клиентские приложения даже при использовании уязвимостей не могут получить права пользователя <code>root</code> и нарушить работу узла.<br />
<br />
Запуск <code>kubernetes</code> версии <code>1.26.3</code> и старше в режиме <code>rootless</code> обеспечивает пакет <code>podsec-k8s</code> версии <code>1.0.5</code> или выше.<br />
<br />
'''Для разворачивания <code>rootless kubernetes</code> требуются ядра ядрах ''5.15'' и выше.'''<br />
<br />
= podsec-k8s - Быстрый старт =<br />
<br />
== Установка master-узла ==<br />
<br />
=== Инициализация master-узла ===<br />
<br />
Для запуска <code>kubernetes</code> в режиме <code>rootless</code> установите пакет <code>podsec-k8s</code> версии <code>1.0.5</code> или выше.<br />
<pre>apt-get install podsec-k8s</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
В каталоге <code>/usr/libexec/podsec/u7s/bin/</code> находятся программы, обеспечивающие работы <code>kubernetes</code><br />
в <code>rootless</code>-режиме.<br />
<br />
Для разворачивания <code>master-узла</code> запустите команду:<br />
<br />
<pre>kubeadm init</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>init</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
После:<br />
<br />
* генерации сертификатов в каталоге <code>/etc/kubernetes/pki</code>,<br />
* загрузки образов, <br />
* генерации conf-файлов в каталоге <code>/etc/kubernetes/manifests/</code>, <code>/etc/kubernetes/manifests/etcd/</code><br />
* запуска сервиса <code>kubelet</code> и <code>Pod</code>’ов системных <code>kubernetes-образов</code><br />
<br />
инициализируется <code>kubernet-кластер</code> из одного узла.<br />
<br />
По окончании скрипт выводит строки подключения <code>master</code>(<code>Control Plane</code>) и <code>worker-узлов</code>:<br />
<pre><br />
You can now join any number of control-plane nodes by copying certificate authorities<br />
and service account keys on each node and then running the following as root:<br />
<br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:.. --control-plane<br />
<br />
Then you can join any number of worker nodes by running the following on each as root:<br />
<br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...<br />
</pre><br />
<br />
=== Запуск сетевого маршрутизатора для контейенеров kube-flannel ===<br />
<br />
Для версии <code>podsec 1.0.8</code> и выше этот шаг выполнять не надо - он выполняется во время <code>kubeadm init</code>. <br />
<br />
Для перевода узла в состояние <code>Ready</code>, запуска <code>coredns</code> <code>Pod</code>’ов запустите <code>flannel</code>.<br />
<br />
На <code>master-узле</code> под пользоваталем <code>root</code> выполните команду:<br />
<br />
<pre># kubectl apply -f /etc/kubernetes/manifests/kube-flannel.yml<br />
Connected to the local host. Press ^] three times within 1s to exit session.<br />
[INFO] Entering RootlessKit namespaces: OK<br />
namespace/kube-flannel created<br />
clusterrole.rbac.authorization.k8s.io/flannel created<br />
clusterrolebinding.rbac.authorization.k8s.io/flannel created<br />
serviceaccount/flannel created<br />
configmap/kube-flannel-cfg created<br />
daemonset.apps/kube-flannel-ds created<br />
Connection to the local host terminated.</pre><br />
После завершения скрипта в течении минуты настраиваются сервисы мастер-узла кластера. По ее истечении проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
=== Проверка работы master-узла ===<br />
<br />
На <code>master-узле</code> выполните команду:<br />
<br />
<pre># kubectl get daemonsets.apps -A<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE<br />
kube-flannel kube-flannel-ds 1 1 1 1 1 &lt;none&gt; 102s<br />
kube-system kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 8h</pre><br />
Число <code>READY</code> каждого <code>daemonset</code> должно быть равно числу <code>DESIRED</code> и должно быть равно числу узлов кластера.<br />
<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host> Ready control-plane 16m v1.26.3 10.96.0.1 <none> ALT SP Server 11100-01 5.15.105-un-def-alt1 cri-o://1.26.2<br />
</pre><br />
Проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
<pre><br />
# kubectl get all -A<br />
NAMESPACE NAME READY STATUS RESTARTS AGE<br />
kube-system pod/coredns-c7df5cd6c-5pkkm 1/1 Running 0 19m<br />
kube-system pod/coredns-c7df5cd6c-cm6vf 1/1 Running 0 19m<br />
kube-system pod/etcd-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-apiserver-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-controller-manager-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-proxy-lqf9c 1/1 Running 0 19m<br />
kube-system pod/kube-scheduler-host-212 1/1 Running 0 19m<br />
<br />
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE<br />
default service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19m<br />
kube-system service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 19m<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE<br />
kube-system daemonset.apps/kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 19m<br />
<br />
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE<br />
kube-system deployment.apps/coredns 2/2 2 2 19m<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY AGE<br />
kube-system replicaset.apps/coredns-c7df5cd6c 2 2 2 19m<br />
</pre><br />
Состояние всех <code>Pod</code>’ов должны быть в <code>1/1</code>.<br />
<br />
Проверьте состояние дерева <code>rootless-процессов</code>:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─nsenter.sh───nsenter───_kubelet.sh───kubelet───11*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-controller───7*[{kube-controller}]<br />
│ │ ├─conmon───kube-apiserver───8*[{kube-apiserver}]<br />
│ │ ├─conmon───kube-scheduler───7*[{kube-scheduler}]<br />
│ │ ├─conmon───etcd───8*[{etcd}]<br />
│ │ ├─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─2*[conmon───coredns───8*[{coredns}]]<br />
│ │ ├─rootlesskit.sh───crio───10*[{crio}]<br />
│ │ └─7*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
...<br />
</pre><br />
Процесс <code>kubelet</code> запускается как сервис в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
Все остальные процессы <code>kube-controller</code>, <code>kube-apiserver</code>, <code>kube-scheduler</code>, <code>kube-proxy</code>, <code>etcd</code>, <code>coredns</code> запускаются как контейнеры от соответствующих образов в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
=== Обеспечение запуска обычных POD’ов на мастер-узле ===<br />
<br />
По умолчанию на master-узле пользовательские <code>Pod</code>ы не запускаются. Чтобы снять это ограничение наберите команду:<br />
<br />
<pre># kubectl taint nodes &lt;host&gt; node-role.kubernetes.io/control-plane:NoSchedule-<br />
node/&lt;host&gt; untainted</pre><br />
== Инициализация и подключение worker-узла ==<br />
<br />
Установите пакет <code>podsec-k8s</code>:<br />
<pre><br />
apt-get install podsec-k8s<br />
</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
<br />
=== Подключение worker-узлов ===<br />
<br />
Скопируйте команду подключения <code>worker-узла</code>, полученную на этапе установки начального <code>master-узла</code>. Запустите ее:<br />
<br />
<pre>kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>join</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
По окончании скрипт выводит текст:<br />
<pre><br />
This node has joined the cluster:<br />
* Certificate signing request was sent to apiserver and a response was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
<br />
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.<br />
</pre><br />
<br />
=== Проверка состояния процессов ===<br />
<br />
Проверьте состояние дерева <code>rootless-процессов</code>:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─nsenter.sh───nsenter───_kubelet.sh───kubelet───10*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─rootlesskit.sh───crio───9*[{crio}]<br />
│ │ └─6*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
...<br />
</pre><br />
Процесс <code>kubelet</code> запускается как сервис в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
Все остальные процессы <code>kube-proxy</code>, <code>kube-flannel</code> запускаются как контейнеры от соответствующих образов в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
=== Проверка готовности master и worker узлов kubernets ===<br />
<br />
Зайдите на <code>master-узел</code> и проверьте подключение <code>worker-узла</code>:</li></ol><br />
<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host1> Ready control-plane 7h54m v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
&lt;host2> Ready &lt;none&gt; 8m30s v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
...<br />
</pre><br />
<br />
== Инициализация и подключение дополнительных master-узлов ==<br />
<br />
Установите пакет <code>podsec-k8s</code>:<br />
<pre><br />
apt-get install podsec-k8s<br />
</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
<br />
=== Подключение master-узлов ===<br />
<br />
Скопируйте команду подключения <code>master-узла</code>, полученную на этапе установки начального <code>master-узла</code>. <br />
Она отличается от команды подключения <code>worker</code>-узлов наличием дополнительных параметров<br />
<code>--control-plane</code>, <code>--certificate-key</code>.<br />
<br />
Запустите ее:<br />
<pre><br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...<br />
--control-plane --certificate-key ... <br />
</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>join</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
<br />
По окончании скрипт выводит текст:<br />
<pre><br />
This node has joined the cluster and a new control plane instance was created:<br />
<br />
* Certificate signing request was sent to apiserver and approval was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
* Control plane label and taint were applied to the new node.<br />
* The Kubernetes control plane instances scaled up.<br />
* A new etcd member was added to the local/stacked etcd cluster.<br />
</pre><br />
<br />
=== Проверка состояния процессов ===<br />
<br />
Проверьте состояние дерева процессов:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─kubelet.sh───nsenter_u7s───nsenter───_kubelet.sh───kubelet───11*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-controller───4*[{kube-controller}]<br />
│ │ ├─conmon───kube-scheduler───8*[{kube-scheduler}]<br />
│ │ ├─conmon───etcd───9*[{etcd}]<br />
│ │ ├─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─conmon───kube-apiserver───8*[{kube-apiserver}]<br />
│ │ ├─rootlesskit.sh───crio───8*[{crio}]<br />
│ │ └─7*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
</pre><br />
<br />
Дерево <code>rootless-процессов</code> должно отличаться от дерева процессов основного <code>master-узла</code><br />
отсутствием контейнеров <code>coredns</code>.<br />
<br />
=== Проверка готовности master и worker узлов kubernets ===<br />
<br />
На одном из master-узлов наберите команду:<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host1> Ready control-plane 7h54m v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
&lt;host2> Ready &lt;none&gt; 8m30s v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
...<br />
&lt;hostN> Ready control-plane 55m v1.26.3 10.96.122.&lt;N> <none> ALT cri-o://1.26.2<br />
...<br />
</pre><br />
<br />
=== Использование REST-интерефейсов подключенных master-узлов ===<br />
<br />
По умолчанию на подключенных <code>master-узлах</code> в файле <code>/etc/kubernetes/admin.conf</code> <br />
указан адрес <code>API-интерфейса</code> основного <code>master-узла</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://<master1>:6443<br />
...<br />
</pre> <br />
<br />
Для балансировки нагрузки в файлах конфигурации <code>~user/.kube/config</code> <br />
есть смысл указать адреса <code>API-интерфейсов</code> дополнительных <code>master-узлов</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://<masterN>:6443<br />
...<br />
</pre><br />
<br />
== Создание гетерогенных кластеров, миграция с rootfull кластеров на rootless кластера ==<br />
<br />
В рамках одного кластера могут функционировать как <code>rootfull</code> узла`, так и <code>rootless узлы</code>.<br />
Например имеет смысл в расках <code>rootfull</code> кластера для повышения защищенности кластера <br />
подключать в качестве <code>Worker</code>оа <code>rootless</code> узлы.<br />
<br />
Перед подключением `rootless` узлов необходимо выполнить определенные действия.<br />
<br />
=== Запуск kube-proxy на rootless-узле в rootfull кластере ===<br />
<br />
Для запуска <code>kube-proxy</code> на <code>rootless-узле</code> в <code>rootfull кластере</code> на <code>ControlPlane</code> узле:<br />
<br />
* Наберите команду редактирования <code>ConfigMap</code>а <code>kube-proxy</code>:<br />
<pre><br />
kubectl -n kube-system edit Configmaps kube-proxy<br />
</pre><br />
<br />
* Измение в элементе <code>data.config.conf</code> значение переменной <code>conntrack.maxPerCore</code> с <code>null</code> на <code>0</code>.<br />
<br />
* Выйдите из редактора<br />
<br />
=== Запуск ControlPlane узла на rootless-узле в rootfull кластере ===<br />
<br />
Для запуска <code>ControlPlane</code> на <code>rootless-узле</code> в <code>rootfull кластере</code> на <code>ControlPlane</code> узле:<br />
<br />
* Наберите команду редактирования <code>ConfigMap</code>а <code>kubeadm-config</code>:<br />
<pre><br />
kubectl -n kube-system edit Configmaps kubeadm-config<br />
</pre><br />
<br />
* Измение в элементе <code>data.ClusterConfiguration</code> значение переменной <code>etcd.local.dataDir</code> с <code>/var/lib/etcd</code> на <code>/var/lib/podsec/u7s/etcd</code>.<br />
<br />
* Выйдите из редактора<br />
<br />
== Получениe строки подключения узла к кластеру ==<br />
<br />
=== Получении строки подключения Worker узла к кластеру ===<br />
<br />
В случае, если команда строки подключения утеряна или срок сгенерированного сертификата<br />
истек можно сгенерировать новую строку подключения, выполнив команду:<br />
<pre><br />
joinCommand=$(/usr/bin/kubeadm token create --print-join-command)<br />
</pre><br />
и выполнить команду подключения:<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
$joinCommand <br />
</pre><br />
<br />
=== Получении строки подключения Control-plane узла к кластеру ===<br />
<br />
В определенных случаях `kubeadm init` генерирует только строку подключения `worker` узлов.<br />
Или срок действия сертификата для подключения истек.<br />
<br />
В этом случае есть смысл перегенерировать сертификат:<br />
<pre><br />
cert=$(/usr/bin/kubeadm init phase upload-certs --upload-certs 2>/dev/null | tail -1)<br />
</pre><br />
строку подключения `control-plane` и `worker` узлов к кластеру:<br />
<pre><br />
joinCommand=$(/usr/bin/kubeadm token create --print-join-command)<br />
</pre><br />
и выполнить команду подключения:<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
$joinCommand --control-plane --certificate-key $cert<br />
</pre><br />
<br />
См. [https://stackoverflow.com/questions/51126164/how-do-i-find-the-join-command-for-kubeadm-on-the-master How do I find the join command for kubeadm on the master?]<br />
<br />
== Системный пользователь u7s-admin ==<br />
<br />
Все контейнеры в <code>rootless kubernetes</code>. включая системные работают от имени системного пользователя <code>u7s-admin</code>.<br />
Вы можете для мониторинга работы системы или запуска дополнительного функционала работать в системе от имени этого пользователя.<br />
<br />
Для входа в терминальный режим этого пользователя достаточно в пользователе с правами <code>root</code> набрать команду:<br />
<pre><br />
# machinectl shell u7s-admin@ /bin/bash<br />
</pre><br />
или задав пароль пользователя:<br />
<pre><br />
# passwd u7s-admin<br />
</pre> <br />
зайти в него через <code>ssh</code>.<br />
<br />
Для входа в namespace пользователя наберите команду :<br />
<pre><br />
$ nsenter_u7s<br />
#<br />
</pre><br />
<br />
В рамках своего <code>namespace</code> пользователь <code>u7s-admin</code> имеет права <code>root</code>, оставаясь в рамках системы <br />
с правам пользователя <code>u7s-admin</code>.<br />
<br />
Наличие прав <code>root</code> позволает использовать системные команды,требующих <code>root-привелегий</code> для работы с сетевым, файловым окружением (эти окружения отличаются от системных): <code>ip</code>, <code>iptables</code>, <code>crictl</code>, ...<br />
<br />
С помощью команды <code>crict</code>l можно <br />
* посмотреть наличие образов в системном кэше, <br />
* удалить, загрузить образы<br />
* посмотреть состояние контейнеров, pod'ов<br />
* и т.п. <br />
<br />
Кроме этого <code>namespace</code> пользователя <code>u7s-admin</code> присутствуют файлы и каталоге созданные в рамках данного <br />
<code>namespace</code> и отсутствующие в основной системе.<br />
Например Вы можете посмотреть логи контейнеров в каталоге <code>/var/log/pods</code> и т.п.<br />
<br />
== Особенности разворачивания приложений в rootless kubernetes ==<br />
<br />
При использовании сервисов типа <code>NodePort</code> поднятые в рамках кластера порты в диапазоне <code>30000-32767</code> остаются в <code>namespace</code> пользователя <code>u7s-admin</code>. Для их проброса наружу необходимо в пользователе <code>u7s-admin</code> запустить команду:<br />
<pre><br />
$ nsenter_u7s rootlessctl add-ports 0.0.0.0:&lt;port>:&lt;port>/tcp<br />
</pre><br />
Сервисы типа <code>NodePort</code> из за их небольшого диапазона и "нестабильности" портов при переносе решения в другой кластер довольно редко используются. Рекомендуется вместо них использовать сервисы типа <code>ClusterIP</code> c доступом к ним через <code>Ingress</code>-контроллеры.<br />
<br />
= Разворачивание rootless kubernetes кластера с балансировщиком REST-запросов haproxy =<br />
<br />
Вышеописанный процесс разворачивания обеспечивать только ручную балансировку запросов:<br />
[[Файл:Variant1.drawio.png|840px||center|rootless kubernetes-кластер без балансировщика haproxy]]<br />
<br />
Ручная балансировка запросов к <code>API-интерфейсам</code> <code>master-узлов</code> путем указания у клиентов адресов различных <br />
<code>master-узлов</code> довольно неудобна, так как не обеспечивает равномерного распределения запросов по узлам кластера и не обеспечивает автоматической отказоустойчивости при выходе из строя <code>master-узлов</code>.<br />
<br />
Решает данную проблему установка балансировщика нагрузки <code>haproxy</code>. <br />
[[Файл:Variant_haproxy_1.drawio.png|840px||center|rootless kubernetes-кластер без балансировщика haproxy]]<br />
<br />
Перевод кластера в режим балансировки запросов через haproxy возможен.<br />
Подробности описаны в статье [https://stackoverflow.com/questions/65505137/how-to-convert-a-kubernetes-non-ha-control-plane-into-an-ha-control-plane How to convert a Kubernetes non-HA control plane into an HA control plane?], но данная процедура не гарантирует корректный перевод на всех версиях <code>kubernetes</code> и ее не рекомендуют применять на <code>production</code> кластерах.<br />
<br />
Так что наиболее надежным способом создания кластера с балансировкой запросов является создание нового кластера. <br />
<br />
== Настройка балансировщика REST-запросов haproxy ==<br />
<br />
Балансировщик <code>REST-запросов haproxy</code> можно устанавливать как на отдельный сервер, так на один из серверов кластера.<br />
[[Файл:Variant haproxy master.drawio.png|840px|center|безрамки]]<br />
<br />
<blockquote><br />
'''Если балансировщик устанавливается на <code>rootless</code> сервер кластера, то для балансировщика необходимо выделить отдельный IP-адрес. Если на этом же сервере функционируют локальный регистратор (<code>registry.local</code>) и сервер подписей (<code>sigstore.local</code>), то IP-адрес балансировщика может совпадать c IP-адресами этих сервисов. <br />
''' <br />
</blockquote><br />
<br />
<blockquote><br />
'''Если планируется создание отказоустойчивого решения на основе нескольких серверов <code>haproxy</code>, то для них кроме собственного <code>IP-адреса</code> необходимо будет для всех серверов <code>haproxy</code> выделить один общий <code>IP-адрес</code>, который будет иметь <code>master-балансировщик</code>.<br />
''' <br />
</blockquote><br />
<br />
<br />
Полная настройка отказоустойчивого кластера <code>haproxy</code> из 3-х узлов описана в документе [https://www.altlinux.org/ALT_Container_OS_%D0%BF%D0%BE%D0%B4%D0%B2%D0%B5%D1%82%D0%BA%D0%B0_K8S._%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_HA_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B0 ALT Container OS подветка K8S. Создание HA кластера].<br />
<br />
Здесь же мы рассмотрим создание и настройка с одним сервером <code>haproxy</code> с балансировкой запросов на <code>master</code>-узлы.<br />
<br />
Установите пакет <code>haproxy</code>:<br />
<br />
<pre># apt-get install haproxy</pre><br />
Отредактируйте конфигурационный файл <code>/etc/haproxy/haproxy.cfg</code>:<br />
<br />
<ul><br />
<li>добавьте в него описание <code>frontend</code>’a <code>main</code>, принимающего запросы по порту <code>8443</code>:<br />
<pre><br />
frontend main<br />
bind *:8443<br />
mode tcp<br />
option tcplog<br />
default_backend apiserver<br />
</pre></li><br />
<li>добавьте описание <code>backend</code>’а <code>apiserver</code>:<br />
<pre><br />
backend apiserver<br />
option httpchk GET /healthz<br />
http-check expect status 200<br />
mode tcp<br />
option ssl-hello-chk<br />
balance roundrobin<br />
server master01 &lt;IP_или_DNS_начального_мастер_узла>:6443 check<br />
</pre></li><br />
<li>запустите <code>haproxy</code>:</li></ul><br />
<br />
<pre># systemctl enable haproxy<br />
# systemctl start haproxy</pre><br />
<br />
== Инициализация master-узла ==<br />
<br />
==== Инициализация мастер-узла при работа с балансировщиков haproxy ====<br />
<br />
При установке начального master-узла необходимо параметром <code>control-plane-endpoint</code> указать URL балансировщика <code>haproxy</code>:<br />
<br />
<pre># kubeadm init --apiserver-advertise-address 192.168.122.80 --control-plane-endpoint &lt;IP_адрес_haproxy&gt;:8443</pre><br />
При запуске в параметре <code>--apiserver-advertise-address</code> укажите IP-адрес API-интерфейса <code>kube-apiserver</code>.<br />
<br />
'''IP-адреса в параметрах''' <code>--apiserver-advertise-address</code> '''и''' <code>--control-plane-endpoint</code> '''должны отличаться. Если Вы развернули''' <code>haproxy</code> '''на том же мастер-узле, поднимите на сетевом нтерфейсе дополнительный IP-адрес и укажите его в параметре''' <code>--control-plane-endpoint</code>'''.<br />
<br />
В результате инициализации <code>kubeadm</code> выведет команды подключения дополнительных <code>control-plane</code> и <code>worker</code> узлов:<br />
<pre><br />
...<br />
You can now join any number of the control-plane node running the following command on each as root:<br />
<br />
kubeadm join &lt;IP_адрес_haproxy>:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:... \<br />
--control-plane --certificate-key ...<br />
<br />
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!<br />
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use<br />
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.<br />
<br />
Then you can join any number of worker nodes by running the following on each as root:<br />
<br />
kubeadm join <IP_адрес_haproxy>:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:...<br />
...<br />
</pre><br />
Обратите внимание - в командах присоединения узлов указывается не URL созданного начального master-узла (<code>&lt;IP_или_DNS_начального_мастер_узла&gt;:6443</code>), а URL <code>haproxy</code>.<br />
<br />
В сформированных файлах конфигурации <code>/etc/kubernetes/admin.conf</code>, <code>~/.kube/config</code> также указывается URL <code>haproxy</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://&lt;IP_адрес_haproxy>:8443<br />
</pre><br />
То есть вся работа с кластеров в дальнейшем идет через балансировщик запросов <code>haproxy</code>.<br />
<br />
Для перевода узла в состояние <code>Ready</code>, запуска coredns Pod’ов запустите flannel<br />
<br />
==== Запуск сетевого маршрутизатора для контейнеров kube-flannel ====<br />
<br />
На <code>master-узле</code> под пользоваталем <code>root</code> выполните команду:<br />
<br />
<pre># kubectl apply -f /etc/kubernetes/manifests/kube-flannel.yml<br />
Connected to the local host. Press ^] three times within 1s to exit session.<br />
[INFO] Entering RootlessKit namespaces: OK<br />
namespace/kube-flannel created<br />
clusterrole.rbac.authorization.k8s.io/flannel created<br />
clusterrolebinding.rbac.authorization.k8s.io/flannel created<br />
serviceaccount/flannel created<br />
configmap/kube-flannel-cfg created<br />
daemonset.apps/kube-flannel-ds created<br />
Connection to the local host terminated.</pre><br />
После завершения скрипта в течении минуты настраиваются сервисы мастер-узла кластера. По ее истечении проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
== Подключение дополнительных master-узлов ==<br />
<br />
<br />
=== Установка тропы PATH поиска исполняемых команд ===<br />
<br />
Измените переменную <code>PATH</code>:<br />
<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
</pre><br />
<br />
=== Подключение master (control plane) узла ===<br />
<br />
Скопируйте строку подключения <code>control-plane</code> узла и вызовите ее:<br />
<br />
<pre># kubeadm join &lt;IP_адрес_haproxy&gt;:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:... \<br />
--control-plane --certificate-key ...</pre><br />
В результате работы команда kubeadm выведет строки:<br />
<pre><br />
This node has joined the cluster and a new control plane instance was created:<br />
<br />
* Certificate signing request was sent to apiserver and approval was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
* Control plane label and taint were applied to the new node.<br />
* The Kubernetes control plane instances scaled up.<br />
* A new etcd member was added to the local/stacked etcd cluster.<br />
...<br />
Run 'kubectl get nodes' to see this node join the cluster.<br />
</pre><br />
Наберите на вновь созданном (или начальном)<code>control-plane</code> узле команду:<br />
<br />
<pre># kubectl get nodes<br />
NAME STATUS ROLES AGE VERSION<br />
&lt;host1&gt; Ready control-plane 4m31s v1.26.3<br />
&lt;host2&gt; Ready control-plane 26s v1.26.3<br />
</pre><br />
Обратите внимание, что роль (ROLES) обоих узлов - <code>control-plane</code>.<br />
<br />
Наберите команду:<br />
<pre><br />
# kubectl get all -A<br />
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
kube-flannel pod/kube-flannel-ds-2mhqg 1/1 Running 0 153m 10.96.0.1 <host1> <none> <none><br />
kube-flannel pod/kube-flannel-ds-95ht2 1/1 Running 0 153m 10.96.122.68 <host2> <none> <none><br />
...<br />
kube-system pod/etcd-<host1> 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/etcd-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-apiserver-<host1> 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-apiserver-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-controller-manager-<host1> 1/1 Running 1 (170m ago) 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-controller-manager-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-proxy-9nbxz 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-proxy-bnmd7 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-scheduler-<host1> 1/1 Running 1 (170m ago) 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-scheduler-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
...<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR<br />
kube-flannel daemonset.apps/kube-flannel-ds 2 2 2 3 3 <none> 153m kube-flannel registry.local/k8s-c10f1/flannel:v0.19.2 app=flannel<br />
kube-system daemonset.apps/kube-proxy 2 2 2 2 2 kubernetes.io/os=linux 174m kube-proxy registry.local/k8s-c10f1/kube-proxy:v1.26.3 k8s-app=kube-proxy<br />
...<br />
</pre><br />
Убедитесь, что сервисы <code>pod/etcd</code>, <code>kube-apiserver</code>, <code>kube-controller-manager</code>, <code>kube-scheduler</code>, <code>kube-proxy</code>, <code>kube-flannel</code> запустились на обоих control-plane узлах.<br />
<br />
=== Добавление master-узла в балансироващик haproxy ===<br />
<br />
Для балансировки запросов по двум серверам добавьте URL подключенного <code>control-plane</code> узла в файл конфигурации <code>/etc/haproxy/haproxy.cfg</code>:<br />
<pre><br />
backend apiserver<br />
option httpchk GET /healthz<br />
http-check expect status 200<br />
mode tcp<br />
option ssl-hello-chk<br />
balance roundrobin<br />
server master01 &lt;IP_или_DNS_начального_мастер_узла>:6443 check<br />
server master02 &lt;IP_или_DNS_подключенного_мастер_узла>:6443 check<br />
</pre><br />
и перезапустите <code>haproxy</code>:<br />
<br />
<pre># systemctl restart haproxy</pre><br />
Логи обращений и балансировку запросов между узлами можно посмотреть командой:<br />
<br />
<pre># tail -f /var/log/haproxy.log</pre><br />
<br />
== Подключение worker-узлов ==<br />
<br />
Подключение дополнительных worker-узлов происходит аналогично описанному выше в главе '''Инициализация и подключение worker-узла'''.<br />
<br />
== Настройка отказоустойчивого кластера серверов haproxy, keepalived ==<br />
<br />
=== Масштабирование haproxy, установка пакетов ===<br />
<br />
Если необходимо создать отказоустойчивое решение допускающее выход <code>haproxy</code>-севрера из строя <br />
установите <code>haproxy</code> на несколько серверов. Файлы конфигурации <code>haproxy<.code> на всех сервервх должны быть идентичны.<br />
<br />
Для контроля доступности <code>haproxy</code> и переназначений виртуального адреса дополнительно установите на каждом сервис <code>keepalived</code>:<br />
<pre><br />
# apt-get install haproxy keepalived<br />
</pre><br />
<br />
=== Конфигурирование keepalived ===<br />
<br />
[[Файл:Variant haproxy keepalived.png|840px|безрамки|центр|kubeenetes кластер с haproxy и keepalived]]<br />
<br />
Создайте файл конфигурации 'keepalived' ''/etc/keepalived/keepalived.conf'':<br />
<br />
! /etc/keepalived/keepalived.conf<br />
! Configuration File for keepalived<br />
global_defs {<br />
router_id LVS_K8S<br />
}<br />
vrrp_script check_apiserver {<br />
script "/etc/keepalived/check_apiserver.sh"<br />
interval 3<br />
weight -2<br />
fall 10<br />
rise 2<br />
}<br />
<br />
vrrp_instance VI_1 {<br />
state MASTER<br />
interface br0<br />
virtual_router_id 51<br />
priority 101<br />
authentication {<br />
auth_type PASS<br />
auth_pass 42<br />
}<br />
virtual_ipaddress {<br />
10.150.0.160 <br />
}<br />
track_script {<br />
check_apiserver<br />
}<br />
}<br />
<br />
На одном из узлов установите параметр ''state'' в значение ''MASTER'' и параметр ''priority'' в значение ''101''.<br />
На остальных параметр ''state'' в значение ''BACKUP'' и параметр ''priority'' в значение ''100''.<br />
<br />
Скрипт ''/etc/keepalived/check_apiserver.sh'' проверяет доступность балансировщика ''haproxy'':<br />
#!/bin/sh<br />
<br />
errorExit() {<br />
echo "*** $*" 1>&2<br />
exit 1<br />
}<br />
<br />
APISERVER_DEST_PORT=8443<br />
APISERVER_VIP=10.150.0.160<br />
curl --silent --max-time 2 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/"<br />
if ip addr | grep -q ${APISERVER_VIP}; then<br />
curl --silent --max-time 2 --insecure https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/"<br />
fi<br />
<br />
Параметр ''APISERVER_DEST_PORT'' задает порт балансировщиков ''haproxy'', параметр ''APISERVER_VIP'' виртуальный адрес,<br />
через который будут взаимодействовать ''master'' (''control plane'') узлы кластера ''k8s''.<br />
<br />
Скрипт проверяет работоспособность ''haproxy'' на локальной машине.<br />
<br />
Подробности см. на [[https://www.altlinux.org/Keepalived]]<br />
А если в настоящее время виртуальный адрес принадлежит текущему узлу, то и работоспособность ''haproxy'' через виртуальный адрес. <br />
<br />
Добавьте флаг на выполнение скрипта:<br />
chmod a+x /etc/keepalived/check_apiserver.sh<br />
<br />
При работающем балансировщике и хотя бы одному доступному порту ''6443'' на ''master-узлах'' скрипт<br />
должен завершаться с кодом ''0''.<br />
<br />
Подробности см. на [[Keepalived]]<br />
<br />
= Установка и настройка ingress-контролера =<br />
<br />
<code>Ingress-контроллер</code> обеспечивает переадресацию <code>http(s)</code> запросов по указанным шаблонам на внутренние сервисы <code>kubernetes-кластера</code>. <br />
Для <code>bare-metal</code> решений и решений на основе виртуальных машин наиболее приемлимым является<br />
[https://github.com/kubernetes/ingress-nginx ingress-nginx контроллер].<br />
<br />
При применении <code>Ingress-контроллера</code> нет необходимости создавать <code>Nodeport-порты</code> и пробрасывать их из <code>namespace</code> пользователя <code>u7s-admin</code>. <code>Ingress-контроллер</code> переадресует <code>http{s)</code> запрос через сервис непосредственно на порты <code>Pod</code>'ов входящих в реплики <code>deployment</code>.<br />
<br />
== Установка и настройка ingress-nginx-контролера в кластере ==<br />
<br />
[[Файл:Ingress.png|840px|безрамки|центр|Использование ingress-контроллера]]<br />
Для установки <code>Ingress-контроллера</code> скопируйте его YAML-манифест:<br />
<pre><br />
curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.0/deploy/static/provider/baremetal/deploy.yaml -o ingress-nginx-deploy.yaml<br />
</pre><br />
<br />
Выберите свободный порт в диапазона <code>30000 - 32767</code> (например <code>31000</code>) и добавьте его в элемент <br />
<code>spec.ports.appProtocol==http</code><br />
Yaml-описании <code>kind==Service</code>:<br />
<pre><br />
...<br />
---<br />
kind: Service<br />
spec:<br />
ports:<br />
- appProtocol: http<br />
...<br />
nodePort: 31000<br />
...<br />
</pre><br />
Если в Вашем решении используется ТОЛЬКО локальный регистратор <code>registry.local</code> <br />
* создайте алиасы образам nginx:<br />
<pre><br />
podman tag registry.k8s.io/ingress-nginx/controller:v1.8.0@sha256:744ae2afd433a395eeb13dc03d3313facba92e96ad71d9feaafc85925493fee3 registry.local/ingress-nginx/controller:v1.8.0<br />
podman tag registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407@sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b registry.local/ingress-nginx/kube-webhook-certgen:v20230407<br />
</pre><br />
и поместите их в локальный регистратор:<br />
<pre><br />
podman push --tls-verify=false --sign-by='<EMAIL>' registry.local/ingress-nginx/controller<br />
podman push --tls-verify=false --sign-by='<EMAIL>' registry.local/ingress-nginx/kube-webhook-certgen<br />
</pre><br />
* исправьте имена образов в скачанном нанифесте на имена образов в локальном регистраторе.<br />
<br />
Запустите Ingress-nginx-контролер:<br />
<pre><br />
kubectl apply -f ingress-nginx-deploy.yaml<br />
</pre><br />
<br />
На одном или нескольких kubernet-узлах (эти узла в дальнейшем нужно прописать в файле конфигурации балансировщика <code>haproxy</code>) пробросьте порт <code>nginx-контроллера</code> (<code>31000</code>) из <code>namespace</code> пользователя <code>u7s-admin</code> в сеть <code>kubernetes</code>:<br />
<pre><br />
nsenter_u7s rootlessctl add-ports 0.0.0.0:31000:31000/tcp<br />
</pre><br />
<br />
=== Настройка Ingress-правил ===<br />
<br />
Kubernetes поддерживает манифесты типа Ingress (kind: Ingress) описывающие правила переадресации запросов URL http-запррса на внутренние порты сервисов (kind: Service) kubernetes. Сервисы в свою очередь перенаправляют запросы на реплики Pod'ов, входящих в данный сервис.<br />
<br />
Общий вид Ingress-манифеста:<br />
<pre><br />
apiVersion: networking.k8s.io/v1<br />
kind: Ingress<br />
metadata:<br />
name: <ingress_имя><br />
spec:<br />
ingressClassName: nginx <br />
rules:<br />
- host: <домен_1><br />
http:<br />
paths:<br />
- path: /<br />
pathType: Prefix<br />
backend:<br />
service:<br />
name: <имя_сервиса_1><br />
port:<br />
number: 80<br />
- path: /<тропа_1><br />
pathType: Prefix<br />
backend:<br />
service:<br />
name: <имя_сервиса_2><br />
port:<br />
number: 80<br />
- host: <домен_2><br />
...<br />
</pre><br />
Где:<br />
* <code>host: <домен_1></code>, <code><домен_2></code>, ... - домены WEB-серверов на которых приходит запрос; <br />
* <code>path:/></code>, <code>path:/<тропа_1></code> - тропы (префиксы запросов после домена) <br />
* <code>pathType: Prefix</code> - тип троп: <code>Prefix</code> или <code>Exact</code>;<br />
* <code>service:</code> - имя сервиса на который перенаправляется запрос, если полученный запрос соответсвует правилу;<br />
* <code>port</code> - номер порта на который перенаправляется запрос.<br />
<br />
Если запросу соответствует несколько правил, выбирается правило с наиболее длинным префиксом.<br />
<br />
Подробности смотри в [https://kubernetes.io/docs/concepts/services-networking/ingress/ Kubernetes: Ingress]<br />
<br />
== Настройка haproxy и DNS ==<br />
<br />
Добавьте в файлы конфигурации <code>haproxy</code> <code>/etc/haproxy/haproxy.conf</code> переадресацию запросов на порт <code>80</code> (<code>http</code>) по IP-адресу балансировщика haproxy на IP-адреса <code>kubernet-узлов</code> на которых выбранный порт <code>nginx-контроллера</code> (<code>31000</code>) проброшен из <code>namespace</code> пользователя <code>u7s-admin</code> в сеть <code>kubernetes</code>:<br />
<pre><br />
frontend http<br />
bind *:80<br />
mode tcp<br />
option tcplog<br />
default_backend http<br />
<br />
backend http<br />
mode tcp<br />
balance roundrobin<br />
server <server1> <ip1>:31000 check<br />
server <server2> <ip2>:31000 check<br />
</pre><br />
<br />
Заведите DNS-запись связывающую DNS-имя http-сервиса с IP-адресам <code>haproxy</code>-сервера.<br />
<br />
= Выбор версии kubernetes, имени регистратора и платформы =<br />
<br />
Во время разворачивания узла командами<br />
<pre><br />
kubeadm init <br />
kubeadm join ...<br />
</pre><br />
или при создании архива образов командой <br />
<pre><br />
podsec-k8s-save-oci ...<br />
</pre><br />
есть возможность установкой переменных среды указать версию kubernetes, имя регистратора и платформы:<br />
* U7S_KUBEVERSION - версия kubernetes (v1.26.9, v1.27.7, ...);<br />
* U7S_REGISTRY - имя регистратора (registry.k8s.io, registry.altlinux.org, registry.local);<br />
* U7S_PLATFORM - имя платформы (k8s-c10f1 , k8s-p10, ...)<br />
<br />
== Выбор версии kubernetes ==<br />
<br />
Начиная с версии <code>1.0.9</code> поддерживается возможность выбора устанавливаемой версии <code>kubernetes</code>.<br />
<br />
=== Указании версии основных kubernetes образов ===<br />
<br />
Основные kubernetes-образы загружаются в момент инициализации узла командой <code>kubeadm</code>.<br />
В список основных образов входят:<br />
<pre><br />
kube-apiserver:<версия_kubernetes><br />
kube-controller-manager:<версия_kubernetes><br />
kube-scheduler:<версия_kubernetes><br />
kube-proxy:<версия_kubernetes><br />
pause:<версия__образа_pause><br />
etcd:<версия__образа_etcd><br />
coredns:<версия__образа_coredns><br />
</pre><br />
<br />
Тег образов <code>kube-*</code> совпадает с полным номером версии kubernetes типа v1.<minor>.<patch>. Например <code>v1.26.9</code>.<br />
<br />
Теги образов <code>pause</code>, <code>etcd</code>, <code>coredns</code> "зашиты" как статические переменные в <code>kubeadm</code> и могут отличаться в разных версиях <code>kubernetes</code>.<br />
<br />
Получить список образов для текущей версии <code<kuvernetes</code> можно командой:<br />
<pre><br />
# /usr/bin/kubeadm config images list 2>/dev/null<br />
</pre><br />
Пример вывода:<br />
<pre><br />
registry.k8s.io/kube-apiserver:v1.26.10<br />
registry.k8s.io/kube-controller-manager:v1.26.10<br />
registry.k8s.io/kube-scheduler:v1.26.10<br />
registry.k8s.io/kube-proxy:v1.26.10<br />
registry.k8s.io/pause:3.9<br />
registry.k8s.io/etcd:3.5.9-0<br />
registry.k8s.io/coredns/coredns:v1.9.3<br />
</pre><br />
<br />
Выбор версии определяет переменная среды <code>U7S_KUBEVERSION</code>.<br />
<br />
На <code>25.03.2024</code> при значении <code>U7S_REGISTRY</code> <code>registry.altlinux.org</code> переменная <code>U7S_KUBEVERSION</code> может принимать следующие значения:<br />
<br />
* Mинор версия v1.26:<br />
* <code>v1.26.6</code>; <br />
* <code>v1.26.9</code>;<br />
* <code>v1.26.11</code>; <br />
<br />
* Mинор версия v1.27:<br />
* <code>v1.27.11</code>;<br />
<br />
* Mинор версия v1.28:<br />
* <code>v1.28.7</code>;<br />
<br />
Данный список относится к версиям регистратора <code>registry.altlinux.org</code>. При использовании нативного регистратора <code>registry.k8s.io</code> (пустое значение <code>export U7S_REGISTRY=</code>) можно указывать любую доступную на <code>registry.k8s.io</code> версию.<br />
<br />
Примеры:<br />
<pre><br />
export U7S_REGISTRY=registry.altlinux.org<br />
export U7S_KUBEVERSION=v1.26.11<br />
</pre><br />
<br />
<pre><br />
export U7S_REGISTRY=registry.local<br />
export U7S_KUBEVERSION=v1.27.11<br />
</pre><br />
<br />
<pre><br />
export U7S_REGISTRY=<br />
export U7S_KUBEVERSION=v1.27.5<br />
</pre><br />
<br />
<br />
По умолчанию (при отсутствии значения переменной <code>U7S_KUBEVERSION</code>) принимается максимальная версия образа <br />
<code>kube-apiserver</code> в рамках минорной версии, которая определяется по минорной версии пакета <code>kubeadm</code>.<br />
<br />
Если номер указанной минорной версии <code>kubernetes</code> отличается от текущего, при вызове команды <code>kubeadm</code><br />
производится удаление текущих <code>rpm-пакетов</code> <code>kubernetes*</code>, <code>cri-o</code> и установка <code>rpm-пакетов</code> указанной версии.<br />
<br />
Возможна ситуация, когда на регистраторе образов отсутствует версия образа, полученная в результате выполнения команды: <br />
<pre><br />
/usr/bin/kubeadm config images list<br />
</pre><br />
Если в переменные среды добавить переменную:<br />
<pre><br />
export U7S_SETAVAILABLEIMAGES=yes<br />
</pre><br />
то в качестве стандартного образа принимается образ с максимальной версией в рамках данной минорной версии <br />
(<code>1.26</code>, <code>1.27</code>, <code>1.28</code>).<br />
Данному образу присваивается тег, полученнфй в результате выполнения команды <code> kubeadm config images</code>.<br />
<br />
=== Указании версии дополнительных kubernetes образов ===<br />
<br />
Кроме основных образов при разворачивании кластера используются дополнительные образы:<br />
* flannel:<U7S_FLANNEL_TAG>;<br />
* flannel-cni-plugin:<U7S_FLANNELCNIPLUGIN_TAG><br />
* cert-manager-webhook:<U7S_CERTMANAGER_TAG>;<br />
* cert-manager-controller:<U7S_CERTMANAGER_TAG>;<br />
* cert-manager-cainjector:<U7S_CERTMANAGER_TAG>.<br />
<br />
Если переменным среды <br />
<code>U7S_FLANNEL_TAG</code>, <br />
<code>U7S_FLANNELCNIPLUGIN_TAG</code>, <br />
<code>U7S_CERTMANAGER_TAG</code> не присвоены значения,<br />
то для каждого образа определяется максимальная версия в регистраторе <br />
и загружается найденная версия образа.<br />
<br />
При необходимости можно изменить версию образа экпортировав перед запускам команды соответствующую переменную. Например:<br />
<pre><br />
export U7S_FLANNEL_TAG=v0.19.2<br />
</pre><br />
<br />
== Выбор исходного регистратора kubernetes-образов ==<br />
<br />
Во время инициализации <code>master-узла</code> кластера (<code>kubeadm init</code>) или во время подключения узла к кластеру (<code>kubeadm join</code>) команда <code>kubeadm</code> может загружать образы с различных регистраторов образов и с различными префиксами.<br />
<br />
Выбор регистратора и префикса образов определяет переменная среды <code>U7S_REGISTRY</code>. <br />
Если переменная не задана регистратор префикс определяется автоматически на основе конфигурационных файлов <code>/etc/os-release</code> и <code>/etc/hosts</code>. <br />
<br />
Переменная среды <code>U7S_REGISTRY</code> может принимать следующие основные значения:<br />
* пустое значение;<br />
* <code>registry.altlinux.org</code>;<br />
* <code>registry.local</code>;<br />
* ...<br />
<br />
=== Нативные kubernetes-образы ===<br />
<br />
<pre><br />
export U7S_REGISTRY=<br />
</pre><br />
Если переменная <code>U7S_REGISTRY</code> установлена в пустое значение образы загружаются со стандартного регистратора образов <code>kubernetes</code>.<br />
<br />
=== Образы altlinux ===<br />
<br />
==== Регистратор registry.altlinux.org ====<br />
<br />
<pre><br />
export U7S_REGISTRY=registry.altlinux.org<br />
</pre><br />
С регистратора <code>altlinux</code> устанавливаются образы при наличии доступа в Интернет.<br />
<br />
==== Локальный регистратор ====<br />
<br />
<pre><br />
export U7S_REGISTRY=registry.local<br />
</pre><br />
<br />
Локальный регистратор используется в сертифицированных дистрибутивах, которые содержат kubernetes-образы на установочном диске.<br />
<br />
Локальный регистратор образов <code>registry.local</code> может обеспечивать:<br />
* разворачивание кластера без доступа в Интернет;<br />
* ускоренное разворачивание как кластера, так и проектов, разворачиваемых в его рамках, так как образы необходимые для запуска <code>Pod</code>'ов загружаются по локальной сети; <br />
* высокий уровень защищенности системы путем установки политик разрешающих загрузку только подписанных образов и только с локального регистратора <code>registry.local</code>.<br />
<br />
Пакет <code>podsec</code> обеспечивает:<br />
* Установку на рабочих местах клиентов и узлах <code>kubernetes</code> политик доступа к образом для различных категория пользователей (скрипт <code>podsec-create-policy</code>).<br />
* Разворачивание на одном узлов локального регистратора образов и сервера подписей образов (скрипт <code>podsec-create-services</code>). <br />
* Загрузку с регистратора <code>registry.altlinux.org</code> образов необходимых для разворачивания <code>kubernetes</code> и формирования максимально сжатого (<200Mb) архива. (скрипты <code>podsec-k8s-save-oci</code>, <code>podsec-save-oci</code>)<br />
* разворачивание образов из архива, их подпись размещение на локальном регистраторе (скрипт <code>podsec-load-sign-oci</code>). <br />
<br />
В зависимости от значения переменных <code>U7S_REGISTRY</code>, <code>U7S_PLATFORM</code>, <code>U7S_KUBEVERSION</code> скрипт <code>podsec-k8s-save-oci</code> формирует архив образов различных версий kubernetes:<br />
* <code>registry.local/k8s-c10f1</code> - архив образов для сертифицированного дистрибутива <code>c10</code> на основе набора образов с регистратора <code>registry.altlinux.org</code> с платформой <code>k8s-c10f1</code>;<br />
* <code>registry.local/k8s-p10</code> - архив образов для несертифицированного дистрибутива <code>p10</code> на основе набора образов с регистратора <code>registry.altlinux.org</code> с платформой <code>k8s-p10</code>;<br />
<br />
Локальный регистратор <code>registry.local</code> может также хранить подписанные образы и запускаемых в рамках кластера проектов. Необходимо только, чтобы каждый образ в рамках локального регистратор <code>registry.local</code> имел префикс. Образы типа <code>registry.local/<имя_образа></code> не допускаются, так как для них трудно определить "подписанта" образа.<br />
<!--<br />
* <code>export U7S_REGISTRY=registry.local/k8s-c10f1</code> - образы для сертифицированного дистрибутива <code>c10</code>;<br />
* <code>export U7S_REGISTRY=registry.local/k8s-p10</code> - образы для несертифицированного дистрибутива <code>p10</code>.<br />
--><br />
<br />
===== podsec-create-policy - настройка политики доступа к образам различным категориям пользователей =====<br />
<br />
'''Формат''':<br />
<pre>podsec-create-policy <ip-адрес_регистратора_и_сервера_подписей></pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт <code>podsec-create-policy</code> формирует в файлах <code>/etc/containers/policy.json</code>, <br />
<code>/etc/containers/registries.d/default.yaml</code> максимально защищенную политику доступа к образам - по умолчанию допускается доступ только к подписанным образам локального регистратора <code>registry.local</code>.<br />
Данная политика распространяется как на пользователей имеющих права суперпользователя, так и на пользователей группы <code>podsec</code>, создаваемые podsec-скриптом <code>podsec-create-podmanusers</code>. <br />
<br />
Пользователи группы <code>podsec-dev</code>, создаваемые podsec-скриптом <code>podsec-create-imagemakeruser</code> имеют неограниченные права на доступ, формирования образов, их подпись и помещение на локальный регистратор <code>registry.local</code>.<br />
<br />
В разворачиваниях kubernetes не требующих таких жестких ограничений в политике доступа и работы с образами политики могут быть смягчены путем модифицирования cистемных файлов политик <code>/etc/containers/policy.json</code>, <code>/etc/containers/registries.d/default.yaml</code> или файлов установки политик пользователей <code>~/.config/containers/policy.json</code>, <code>~/.config/containers/registries.d/default.yaml</code>.<br />
<br />
===== podsec-create-services - разворачивание локального регистратора образов и сервера подписей образов =====<br />
<br />
Скрипт <code>podsec-create-services</code> обеспечивает разворачивание локального регистратора образов и сервера подписей образов.<br />
<br />
===== Поддержка электронной подписи образов =====<br />
<br />
Для <code>kubernetes-образов</code>, хранящихся в архиве образов распаковку образов, их подпись и размещение на локальном регистраторе <code>registry.local</code> обеспечивает скрипт <code>podsec-load-sign-oci</code> запускаемый пользователем группы <code>podsec-dev</code>.<br />
<br />
Для других образов пользователь группы <code>podsec-dev</code> должен создать образ в домене локального регистратора <code>registry.local/</prefix>/</code> и поместить его в регистратор командой:<br />
<pre>podman push --tls-verify=false --sign-by="<email-подписанта" <образ></pre><br />
<br />
Образ в домене <code>registry.local/</prefix>/</code> может быть получен:<br />
* присваивании алиаса стороннему образу:<br />
<pre>podman tag <сторонний_образ> registry.local/</prefix>/<локальный_образ></pre><br />
* сборки образов через <code>Dockerfile</code>.<br />
<pre>podman build -t registry.local/</prefix>/<локальный_образ> ...</pre><br />
<br />
== Указание платформы ==<br />
<br />
Кроме имени регистратора kubernetes-образы altlinux содержит в имени (например registry.altlinux.org/k8s-p10/kube-apiserver) название платформы:<br />
* k8s-p10 - образы для дистрибутива p10;<br />
* k8s-c10f1 - образы сертифицированного дистрибутива c10;<br />
* test_k8s - тестовые образы;<br />
* ...<br />
<br />
Платформу устанавливаемых образов можно указать в переменной <code>U7S_PLATFORM</code>. Например:<br />
<pre><br />
export U7S_PLATFORM=test_k8s<br />
</pre><br />
<br />
== Автоматический выбор регистратора образов и платформы ==<br />
<br />
Если переменная <code>U7S_REGISTRY</code> не установлена, ее значения вычисляется автоматически по следующему алгоритму:<br />
<br />
* Если файл <code>/etc/hosts</code> содержит описание хоста <code>registry.local</code> префикс переменной <code>U7S_REGISTRY</code> принимает значение <code>registry.local/</code>, иначе <code>registry.altlinux.org/</code>.<br />
* Если переменная <code>CPE_NAME</code> файла <code>/etc/os-release</code> содержит значение <code>spserver</code> суффикс переменной <code>U7S_PLATFORM</code> принимает значение <code>k8s-c10f1</code>, иначе <code>k8s-p10</code>.<br />
<br />
= podsec-k8s-rbac - Поддержка управление доступом на основе ролей (RBAC) =<br />
<br />
В пакет <code>podsec-k8s-rbac</code> входит набор скриптов для работы с <code>RBAC</code> - <code>Role Based Access Control</code>:<br />
* <code>podsec-k8s-rbac-create-user</code> - создание <code>RBAC-пользователя</code>;<br />
* <code>podsec-k8s-rbac-create-kubeconfig</code> - создание ключей, сертификатов и файла конфигурации <code>RBAC-пользователя</code>;<br />
* <code>podsec-k8s-rbac-create-remoteplace</code> - создание удаленного рабочего места;<br />
* <code>podsec-k8s-rbac-bindrole</code> - привязывание пользователя к кластерной или обычной роли;<br />
* <code>podsec-k8s-rbac-get-userroles</code> - получить список кластерные и обычных ролей пользователя;<br />
* <code>podsec-k8s-rbac-unbindrole</code> - отвязывание пользователя от кластерной или обычной роли.<br />
<br />
== podsec-k8s-rbac-create-user - создание RBAC-пользователя ==<br />
<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-user имя_пользователя</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт:<br />
* создает RBAC пользователя<br />
* создает в домашнем директории каталог .kube<br />
* устанавливаются соответствующие права доступа к каталогам.<br />
<br />
== podsec-k8s-rbac-create-kubeconfig - создание ключей, сертификатов и файла конфигурации RBAC-пользователя ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-kubeconfig имя_пользователя[@<имя_удаленного_пользователя>] [группа ...]</pre><br />
<br />
'''Описание''':<br />
Скрипт должен вызываться администратором безопасности средства контейнеризации.<br />
<br />
Для <code>rootless</code> решения имя удаленного пользователя принимается <code>u7s-admin</code>.<br />
<br />
Для <code>rootfull</code> решения необходимо после символа <code>@</code> указать <code>имя удаленного пользователя</code>.<br />
<br />
Скрипт в каталоге <code>~имя_пользователя/.kube производит</code>:<br />
* Создании личного (private) ключа пользователя (файл <code>имя_пользователя.key</code>).<br />
* Создание запроса на подпись сертификата (CSR) (файл <code>имя_пользователя.key</code>).<br />
* Запись <code>запроса на подпись сертификата CSR </code>в кластер.<br />
* Подтверждение <code>запроса на подпись сертификата (CSR)</code>.<br />
* Создание <code>сертификата</code> (файл <code>имя_пользователя.crt</code>).<br />
* Проверку корректности сертификата<br />
* Формирование файла конфигурации пользователя (файл <code>config</code>)<br />
* Добавление контекста созданного пользователя<br />
<br />
== podsec-k8s-rbac-create-remoteplace - создание удаленного рабочего места ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-remoteplace ip-адрес</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит настройку удаленного рабочего места пользователя путем копирования его конфигурационного файла.<br />
<br />
== podsec-k8s-rbac-bindrole - привязывание пользователя к кластерной или обычной роли ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-bindrole имя_пользователя role|role=clusterrole|clusterrole роль имя_связки_роли [namespace]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит привязку пользователя к обычной или кластерной роли используя имя_связки_роли.<br />
<br />
'''Параметры''':<br />
<br />
* имя_пользователя должно быть создано командой podsec-k8s-rbac-create-user и сконфигурировано на доступ к кластеру командой podsec-k8s-rbac-create-kubeconfig;<br />
* тип роли может принимать следующие значения:<br />
* role - пользователь привязывется к обычной роли с именем <роль> (параметр namespace в этом случае обязателен);<br />
* role=clusterrole - пользователь привязывется к обычной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае обязателен);<br />
* clusterrole - пользователь привязывется к кластерной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае должен отсутствовать).<br />
* роль - имя обычной или кластерной роли в зависимости от предыдущего параметра;<br />
* имя_связки_роли - имя объекта класса rolebindings или clusterrolebindings в зависимости от параметра тип роли. В рамках этого объекта к кластерной или обычной роли могут быть привязаны несколько пользователей.<br />
* namespace - имя namespace для обычной роли.<br />
<br />
== podsec-k8s-rbac-get-userroles - получить список кластерные и обычных ролей пользователя ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-get-userroles имя_пользователя [showRules]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт формирует список кластерные и обычных ролей которые связаны с пользователем. При указании флага <code>showRules</code>, для каждой роли указывается список правил ("<code>rules:[...]</code>"), которые принадлежат каждой роли пользователя.<br />
<br />
Результат возвращается в виде <code>json-строки</code> формата:<br />
<pre><br />
{<br />
"": {<br />
"clusterRoles": [...],<br />
"roles": {<br />
"allNamespaces": [...],<br />
"namespaces": [<br />
{<br />
"": [...],<br />
...<br />
}<br />
}<br />
}<br />
}<br />
</pre><br />
Где <code>[...]</code> - массив объектов типа:<br />
<pre><br />
{<br />
"bindRoleName": "",<br />
"bindedRoleType": "ClusterRole|Role",<br />
"bindedRoleName": "",<br />
"unbindCmd": "podsec-k8s-rbac-unbindrole ..."<br />
}<br />
</pre><br />
<br />
== podsec-k8s-rbac-unbindrole - отвязывание пользователя от кластерной или обычной роли ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-unbindrole имя_пользователя role|clusterrole роль имя_связки_роли [namespace]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит отвязку роли от кластерной или обычной роли, созданной командой <code>podsec-k8s-rbac-bindrole</code>. Полный текст команды можно получить в выводе команды <code>podsec-k8s-rbac-get-userroles</code> в поле <code>unbindCmd</code>. Если в указанном имя_связки_роли объекте класса <code>rolebindings</code> или <code>clusterrolebindings</code> еще остаются пользователи - объект модифицируется. Если список становится пуст - объект удаляется.<br />
<br />
'''Параметры''':<br />
<br />
* <code>имя_пользователя</code> должно быть создано командой <code>podsec-k8s-rbac-create-user</code> и сконфигурировано на доступ к кластеру командой <code>podsec-k8s-rbac-create-kubeconfig</code>;<br />
* тип роли может принимать следующие значения:<br />
* <code>role</code> - пользователь привязывается к обычной роли с именем <роль> (параметр <code>namespace</code> в этом случае обязателен);<br />
* <code>clusterrole</code> - пользователь привязывается к кластерной роли используя кластерную роль с именем <роль> (параметр <code>namespace</code> в этом случае должен отсутствовать).<br />
* <code>роль</code> - имя обычной или кластерной роли в зависимости от предыдущего параметра;<br />
* <code>имя_связки_роли</code> - имя объекта класса <code>rolebindings</code> или <code>clusterrolebindings</code> в зависимости от параметра тип роли. В рамках этого объекта к кластерной или обычной роли могут быть привязаны несколько пользователей.<br />
* <code>namespace</code> - имя <code>namespace</code> для обычной роли.<br />
<br />
= podsec-inotify - Мониторинг безопасности системы =<br />
<br />
В пакет podsec-inotify входит набор скриптов для мониторинга безопасности системы: <br />
* podsec-inotify-check-policy - проверка настроек политики контейнеризации на узле;<br />
* podsec-inotify-check-containers - проверка наличия изменений файлов в директориях rootless контейнерах;<br />
* podsec-inotify-check-images - проверка образов на предмет их соответствия настройки политикам контейнеризации на узле;<br />
* podsec-inotify-check-kubeapi - мониторинг аудита API-интерфейса kube-apiserver control-plane узла;<br />
* podsec-inotify-check-vuln - мониторинг docker-образов узла сканером безопасности trivy.<br />
<br />
== podsec-inotify-check-policy - проверка настроек политики контейнеризации на узле ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-policy [-v[vv]] [-a интервал] [-f интервал] -c интервал -h интервал [-m интервал] х-w интервалъ [-l интервал] [-d интервал]</pre><br />
<br />
'''Описание''':<br />
Плугин проверяет настройки политики контейнеризации на узле.<br />
<br />
Проверка идет по следующим параметрам:<br />
<br />
* файл <code>policy.json</code> установки транспортов и политик доступа к регистраторам:<br />
{| class="wikitable"<br />
|+<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| имеющих <code>defaultPolicy != reject</code>, но не входящих в группу <code>podman_dev</code> || 102<br />
|-<br />
| не имеющих не имеющих <code>registry.local</code> в списке регистраторов для которых проверяется наличие электронной подписи образов || 103<br />
|-<br />
| имеющих в политике регистраторы для которых не проверяется наличие электронной подписи образов || 104<br />
|-<br />
| имеющих в списке поддерживаемых транспорты отличные от <code>docker</code> (транспорт получения образов с регистратора) || 105<br />
|}<br />
<br />
<br />
* файлы привязки регистраторов к серверам хранящим электронные подписи (файл привязки о умолчанию <code>default.yaml</code> и файлы привязки регистраторов <code>*.yaml</code> каталога <code>registries.d</code>). Наличие (число) пользователей:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| не использующих хранилище подписей <code>http://sigstore.local:81/sigstore/</code> как хранилище подписей по умолчанию || 106<br />
|}<br />
<br />
* контроль групп пользователей<br />
# наличие пользователей имеющих образы, но не входящих в группу <code>podman</code>:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| наличие пользователей имеющих образы, но не входящих в группу <code>podman</code> || 101<br />
|}<br />
# наличие пользователей группы <code>podman</code> (за исключением входящих в группу <code>podman_dev</code>):<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| входящих в группу <code>wheel</code> || 101<br />
|-<br />
| имеющих каталог <code>.config/containers/</code> открытым на запись и изменения || 90 * <code>доля_нарушителей</code><br />
|-<br />
| не имеющих файла конфигурации <code>.config/containers/storage.conf</code> || 90 * <code>доля_нарушителей</code><br />
|}<br />
<code>доля_нарушителей</code> считается как: <code>число_нарушителей / число_пользователей_группы_podman</code><br />
<br />
Все веса метрик суммируются и формируется итоговая метрика.<br />
<br />
== podsec-inotify-check-containers - проверка наличия изменений файлов в директориях rootless контейнерах ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-containers</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт:<br />
* создаёт список директорий <code>rootless</code> контейнеров, существующих в системе,<br />
* запускает проверку на добавление,удаление, и изменение файлов в директориях контейнеров,<br />
* отсылает уведомление об изменении в системный лог.<br />
<br />
== podsec-inotify-check-images - проверка образов на предмет их соответствия настройки политикам контейнеризации на узле ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-images [-v[vv]] [-a интервал] [-f интервал] -c интервал -h интервал [-m интервал] х-w интервалъ [-l интервал] [-d интервал]</pre><br />
<br />
'''Описание''':<br />
<br />
Плугин проверяет образы на предмет их соответствия настройки политикам контейнеризации на узле. Проверка идет по следующим параметрам:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| наличие в политике пользователя регистраторов не поддерживающие электронную подпись || 101<br />
|-<br />
| наличие в кэше образов неподписанных образов || 101<br />
|-<br />
| наличие в кэше образов вне поддерживаемых политик || 101<br />
|}<br />
Все веса метрик суммируются и формируется итоговая метрика.<br />
<br />
== podsec-inotify-check-kubeapi - мониторинг аудита API-интерфейса kube-apiserver control-plane узла ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-kubeapi [-d]</pre><br />
<br />
'''Описание''':<br />
Скрипт производит мониторинг файла <code>/etc/kubernetes/audit/audit.log</code> аудита API-интерфейса <code>kube-apiserver</code>.<br />
<br />
Политика аудита располагается в файле <code>/etc/kubernetes/audit/policy.yaml</code>:<br />
<pre><br />
apiVersion: audit.k8s.io/v1<br />
kind: Policy<br />
omitManagedFields: true<br />
rules:<br />
# do not log requests to the following <br />
- level: None<br />
nonResourceURLs:<br />
- "/healthz*"<br />
- "/logs"<br />
- "/metrics"<br />
- "/swagger*"<br />
- "/version"<br />
- "/readyz"<br />
- "/livez"<br />
<br />
- level: None<br />
users:<br />
- system:kube-scheduler<br />
- system:kube-proxy<br />
- system:apiserver<br />
- system:kube-controller-manager<br />
- system:serviceaccount:gatekeeper-system:gatekeeper-admin<br />
<br />
- level: None<br />
userGroups:<br />
- system:nodes<br />
- system:serviceaccounts<br />
- system:masters<br />
<br />
# limit level to Metadata so token is not included in the spec/status<br />
- level: Metadata<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: authentication.k8s.io<br />
resources:<br />
- tokenreviews<br />
<br />
# extended audit of auth delegation<br />
- level: RequestResponse<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: authorization.k8s.io<br />
resources:<br />
- subjectaccessreviews<br />
<br />
# log changes to pods at RequestResponse level<br />
- level: RequestResponse<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: "" # core API group; add third-party API services and your API services if needed<br />
resources: ["pods"]<br />
verbs: ["create", "patch", "update", "delete"]<br />
<br />
# log everything else at Metadata level<br />
- level: Metadata<br />
omitStages:<br />
- RequestReceived<br />
</pre> <br />
<br />
Текущие настройки производят логирование всех обращений "несистемных" пользователей (в том числе анонимных) к ресурсам <code>kubernetes</code>.<br />
<br />
Скрипт производит выборку всех обращений, в ответ на которые был сформирован код более <code>400</code> - запрет доступа. <br />
Все эти факты записываются в системный журнал и накапливаются в файле логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code>, который периодически передается через посту системному адмиристратору.<br />
<br />
'''Параметры''':<br />
<br />
* <code>-d</code> - скирпт запускается в режиме демона, производящего онлайн мониторинг файла <code>/etc/kubernetes/audit/audit.log</code> и записывающего факты запросов с запретом доступа в системный журнал и файл логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code>.<br />
<br />
* при запуске без параметров скрипт посылает файл логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code> почтой системному администратору (пользователь <code>root</code>) и обнуляет файл логов.<br />
<br />
В состав пакета кроме этого скрипта входят:<br />
<br />
* файл описания сервиса </code>/lib/systemd/system/podsec-inotify-check-kubeapi.service</code>. Для его запуска екобходимо выполнить команды:<br />
<pre> <br />
# systemctl enable podsec-inotify-check-kubeapi.service<br />
# systemctl start podsec-inotify-check-kubeapi.service<br />
</pre><br />
<br />
* файл для </code>cron</code> </code>/etc/podsec/crontabs/podsec-inotify-check-kubeapi</code>. Файл содержит единственную строку с описанием режима запуска скрипта </code>podsec-inotify-check-kubeapi</code> для передачи почты системному администратору. <br />
Скрипт запускается один раз в 10 минут.<br />
Во время установки пакета строка файла (в случае ее отсутствия) дописыватся в </code>crontab</code>-файл </code>/var/spool/cron/root</code> пользователя </code>root</code>. <br />
Если необходимо изменить режим запуска скрипта или выключить его это можно сделать командой редактирования </code>crontab</code>-файла:<br />
<pre><br />
# crontab -e<br />
</pre><br />
<br />
== podsec-inotify-check-vuln - мониторинг docker-образов узла сканером безопасности trivy ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-vuln</pre><br />
<br />
'''Описание''':<br />
<br />
<br />
<br />
Для корректной работы скрипта необходимо запустить сервис <code>podsec-inotify-server-trivy</code>:<br />
<pre><br />
systemctl enable --now podsec-inotify-server-trivy<br />
</pre><br />
<br />
Скрипт производит мониторинг <code>docker-образов</code> узла сканером безопасности <code>trivy</code>:<br />
<br />
* Если скрипт запускается от имени пользователя <code>root</code> скрипт:<br />
# проверяет сканером <code>trivy</code> <code>rootfull</code> образы;<br />
# для всех пользователей каталога <code>/home/</code> проверяется наличие <code>rootless</code>-образов. При их наличии проверяет сканером <code>trivy</code> эти образы.<br />
<br />
* Если скрипт запускается от имени обычного пользователя проверяется наличие <code>rootless</code>-образов. При их наличии проверяет сканером <code>trivy</code> эти образы.<br />
<br />
Результат анализа посылается в системный лог.<br />
Если при анализе образа число обнаруженных угроз уровня <code>HIGH</code> больше 0, результат посылается почтой системному администратору (<code>root</code>).<br />
<br />
'''Параметры''':<br />
<br />
Отсутствуют.<br />
<br />
В состав пакета кроме этого скрипта входит файл для <code>cron</code> <code>/etc/podsec/crontabs/podsec-inotify-check-vuln</code>. Файл содержит единственную строку с описанием режима запуска скрипта <code>podsec-inotify-check-vuln</code>.<br />
Во время установки пакета строка файла (в случае ее отсутствия) дописыватся в <code>crontab</code>-файл <code>/var/spool/cron/root</code> пользователя <code>root</code>.<br />
<br />
Если необходимо изменить режим запуска скрипта или выключить его это можно сделать командой редактирования <code>crontab</code>-файла:<br />
<pre><br />
# crontab -e<br />
</pre></div>Kafhttps://www.altlinux.org/index.php?title=Rootless_kubernetes&diff=78992Rootless kubernetes2024-03-25T17:17:14Z<p>Kaf: /* Указании версии дополнительных kubernetes образов */</p>
<hr />
<div><br />
<br />
= Общее описание =<br />
<br />
Запуск <code>Kubernetes</code> в режиме <code>rootless</code> обеспечивает запуск <code>Pod</code>ов без системных <code>root</code>-привелегий в рамках <code>user namespace</code> системного пользователя <code>u7s-admin</code>. Работа в этом режиме практически не требует никаких модификаций, но обеспечивает повышенные уровень защищенности <code>kubernetes</code>, так как клиентские приложения даже при использовании уязвимостей не могут получить права пользователя <code>root</code> и нарушить работу узла.<br />
<br />
Запуск <code>kubernetes</code> версии <code>1.26.3</code> и старше в режиме <code>rootless</code> обеспечивает пакет <code>podsec-k8s</code> версии <code>1.0.5</code> или выше.<br />
<br />
'''Для разворачивания <code>rootless kubernetes</code> требуются ядра ядрах ''5.15'' и выше.'''<br />
<br />
= podsec-k8s - Быстрый старт =<br />
<br />
== Установка master-узла ==<br />
<br />
=== Инициализация master-узла ===<br />
<br />
Для запуска <code>kubernetes</code> в режиме <code>rootless</code> установите пакет <code>podsec-k8s</code> версии <code>1.0.5</code> или выше.<br />
<pre>apt-get install podsec-k8s</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
В каталоге <code>/usr/libexec/podsec/u7s/bin/</code> находятся программы, обеспечивающие работы <code>kubernetes</code><br />
в <code>rootless</code>-режиме.<br />
<br />
Для разворачивания <code>master-узла</code> запустите команду:<br />
<br />
<pre>kubeadm init</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>init</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
После:<br />
<br />
* генерации сертификатов в каталоге <code>/etc/kubernetes/pki</code>,<br />
* загрузки образов, <br />
* генерации conf-файлов в каталоге <code>/etc/kubernetes/manifests/</code>, <code>/etc/kubernetes/manifests/etcd/</code><br />
* запуска сервиса <code>kubelet</code> и <code>Pod</code>’ов системных <code>kubernetes-образов</code><br />
<br />
инициализируется <code>kubernet-кластер</code> из одного узла.<br />
<br />
По окончании скрипт выводит строки подключения <code>master</code>(<code>Control Plane</code>) и <code>worker-узлов</code>:<br />
<pre><br />
You can now join any number of control-plane nodes by copying certificate authorities<br />
and service account keys on each node and then running the following as root:<br />
<br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:.. --control-plane<br />
<br />
Then you can join any number of worker nodes by running the following on each as root:<br />
<br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...<br />
</pre><br />
<br />
=== Запуск сетевого маршрутизатора для контейенеров kube-flannel ===<br />
<br />
Для версии <code>podsec 1.0.8</code> и выше этот шаг выполнять не надо - он выполняется во время <code>kubeadm init</code>. <br />
<br />
Для перевода узла в состояние <code>Ready</code>, запуска <code>coredns</code> <code>Pod</code>’ов запустите <code>flannel</code>.<br />
<br />
На <code>master-узле</code> под пользоваталем <code>root</code> выполните команду:<br />
<br />
<pre># kubectl apply -f /etc/kubernetes/manifests/kube-flannel.yml<br />
Connected to the local host. Press ^] three times within 1s to exit session.<br />
[INFO] Entering RootlessKit namespaces: OK<br />
namespace/kube-flannel created<br />
clusterrole.rbac.authorization.k8s.io/flannel created<br />
clusterrolebinding.rbac.authorization.k8s.io/flannel created<br />
serviceaccount/flannel created<br />
configmap/kube-flannel-cfg created<br />
daemonset.apps/kube-flannel-ds created<br />
Connection to the local host terminated.</pre><br />
После завершения скрипта в течении минуты настраиваются сервисы мастер-узла кластера. По ее истечении проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
=== Проверка работы master-узла ===<br />
<br />
На <code>master-узле</code> выполните команду:<br />
<br />
<pre># kubectl get daemonsets.apps -A<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE<br />
kube-flannel kube-flannel-ds 1 1 1 1 1 &lt;none&gt; 102s<br />
kube-system kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 8h</pre><br />
Число <code>READY</code> каждого <code>daemonset</code> должно быть равно числу <code>DESIRED</code> и должно быть равно числу узлов кластера.<br />
<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host> Ready control-plane 16m v1.26.3 10.96.0.1 <none> ALT SP Server 11100-01 5.15.105-un-def-alt1 cri-o://1.26.2<br />
</pre><br />
Проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
<pre><br />
# kubectl get all -A<br />
NAMESPACE NAME READY STATUS RESTARTS AGE<br />
kube-system pod/coredns-c7df5cd6c-5pkkm 1/1 Running 0 19m<br />
kube-system pod/coredns-c7df5cd6c-cm6vf 1/1 Running 0 19m<br />
kube-system pod/etcd-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-apiserver-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-controller-manager-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-proxy-lqf9c 1/1 Running 0 19m<br />
kube-system pod/kube-scheduler-host-212 1/1 Running 0 19m<br />
<br />
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE<br />
default service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19m<br />
kube-system service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 19m<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE<br />
kube-system daemonset.apps/kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 19m<br />
<br />
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE<br />
kube-system deployment.apps/coredns 2/2 2 2 19m<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY AGE<br />
kube-system replicaset.apps/coredns-c7df5cd6c 2 2 2 19m<br />
</pre><br />
Состояние всех <code>Pod</code>’ов должны быть в <code>1/1</code>.<br />
<br />
Проверьте состояние дерева <code>rootless-процессов</code>:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─nsenter.sh───nsenter───_kubelet.sh───kubelet───11*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-controller───7*[{kube-controller}]<br />
│ │ ├─conmon───kube-apiserver───8*[{kube-apiserver}]<br />
│ │ ├─conmon───kube-scheduler───7*[{kube-scheduler}]<br />
│ │ ├─conmon───etcd───8*[{etcd}]<br />
│ │ ├─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─2*[conmon───coredns───8*[{coredns}]]<br />
│ │ ├─rootlesskit.sh───crio───10*[{crio}]<br />
│ │ └─7*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
...<br />
</pre><br />
Процесс <code>kubelet</code> запускается как сервис в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
Все остальные процессы <code>kube-controller</code>, <code>kube-apiserver</code>, <code>kube-scheduler</code>, <code>kube-proxy</code>, <code>etcd</code>, <code>coredns</code> запускаются как контейнеры от соответствующих образов в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
=== Обеспечение запуска обычных POD’ов на мастер-узле ===<br />
<br />
По умолчанию на master-узле пользовательские <code>Pod</code>ы не запускаются. Чтобы снять это ограничение наберите команду:<br />
<br />
<pre># kubectl taint nodes &lt;host&gt; node-role.kubernetes.io/control-plane:NoSchedule-<br />
node/&lt;host&gt; untainted</pre><br />
== Инициализация и подключение worker-узла ==<br />
<br />
Установите пакет <code>podsec-k8s</code>:<br />
<pre><br />
apt-get install podsec-k8s<br />
</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
<br />
=== Подключение worker-узлов ===<br />
<br />
Скопируйте команду подключения <code>worker-узла</code>, полученную на этапе установки начального <code>master-узла</code>. Запустите ее:<br />
<br />
<pre>kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>join</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
По окончании скрипт выводит текст:<br />
<pre><br />
This node has joined the cluster:<br />
* Certificate signing request was sent to apiserver and a response was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
<br />
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.<br />
</pre><br />
<br />
=== Проверка состояния процессов ===<br />
<br />
Проверьте состояние дерева <code>rootless-процессов</code>:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─nsenter.sh───nsenter───_kubelet.sh───kubelet───10*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─rootlesskit.sh───crio───9*[{crio}]<br />
│ │ └─6*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
...<br />
</pre><br />
Процесс <code>kubelet</code> запускается как сервис в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
Все остальные процессы <code>kube-proxy</code>, <code>kube-flannel</code> запускаются как контейнеры от соответствующих образов в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
=== Проверка готовности master и worker узлов kubernets ===<br />
<br />
Зайдите на <code>master-узел</code> и проверьте подключение <code>worker-узла</code>:</li></ol><br />
<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host1> Ready control-plane 7h54m v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
&lt;host2> Ready &lt;none&gt; 8m30s v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
...<br />
</pre><br />
<br />
== Инициализация и подключение дополнительных master-узлов ==<br />
<br />
Установите пакет <code>podsec-k8s</code>:<br />
<pre><br />
apt-get install podsec-k8s<br />
</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
<br />
=== Подключение master-узлов ===<br />
<br />
Скопируйте команду подключения <code>master-узла</code>, полученную на этапе установки начального <code>master-узла</code>. <br />
Она отличается от команды подключения <code>worker</code>-узлов наличием дополнительных параметров<br />
<code>--control-plane</code>, <code>--certificate-key</code>.<br />
<br />
Запустите ее:<br />
<pre><br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...<br />
--control-plane --certificate-key ... <br />
</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>join</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
<br />
По окончании скрипт выводит текст:<br />
<pre><br />
This node has joined the cluster and a new control plane instance was created:<br />
<br />
* Certificate signing request was sent to apiserver and approval was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
* Control plane label and taint were applied to the new node.<br />
* The Kubernetes control plane instances scaled up.<br />
* A new etcd member was added to the local/stacked etcd cluster.<br />
</pre><br />
<br />
=== Проверка состояния процессов ===<br />
<br />
Проверьте состояние дерева процессов:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─kubelet.sh───nsenter_u7s───nsenter───_kubelet.sh───kubelet───11*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-controller───4*[{kube-controller}]<br />
│ │ ├─conmon───kube-scheduler───8*[{kube-scheduler}]<br />
│ │ ├─conmon───etcd───9*[{etcd}]<br />
│ │ ├─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─conmon───kube-apiserver───8*[{kube-apiserver}]<br />
│ │ ├─rootlesskit.sh───crio───8*[{crio}]<br />
│ │ └─7*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
</pre><br />
<br />
Дерево <code>rootless-процессов</code> должно отличаться от дерева процессов основного <code>master-узла</code><br />
отсутствием контейнеров <code>coredns</code>.<br />
<br />
=== Проверка готовности master и worker узлов kubernets ===<br />
<br />
На одном из master-узлов наберите команду:<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host1> Ready control-plane 7h54m v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
&lt;host2> Ready &lt;none&gt; 8m30s v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
...<br />
&lt;hostN> Ready control-plane 55m v1.26.3 10.96.122.&lt;N> <none> ALT cri-o://1.26.2<br />
...<br />
</pre><br />
<br />
=== Использование REST-интерефейсов подключенных master-узлов ===<br />
<br />
По умолчанию на подключенных <code>master-узлах</code> в файле <code>/etc/kubernetes/admin.conf</code> <br />
указан адрес <code>API-интерфейса</code> основного <code>master-узла</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://<master1>:6443<br />
...<br />
</pre> <br />
<br />
Для балансировки нагрузки в файлах конфигурации <code>~user/.kube/config</code> <br />
есть смысл указать адреса <code>API-интерфейсов</code> дополнительных <code>master-узлов</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://<masterN>:6443<br />
...<br />
</pre><br />
<br />
== Создание гетерогенных кластеров, миграция с rootfull кластеров на rootless кластера ==<br />
<br />
В рамках одного кластера могут функционировать как <code>rootfull</code> узла`, так и <code>rootless узлы</code>.<br />
Например имеет смысл в расках <code>rootfull</code> кластера для повышения защищенности кластера <br />
подключать в качестве <code>Worker</code>оа <code>rootless</code> узлы.<br />
<br />
Перед подключением `rootless` узлов необходимо выполнить определенные действия.<br />
<br />
=== Запуск kube-proxy на rootless-узле в rootfull кластере ===<br />
<br />
Для запуска <code>kube-proxy</code> на <code>rootless-узле</code> в <code>rootfull кластере</code> на <code>ControlPlane</code> узле:<br />
<br />
* Наберите команду редактирования <code>ConfigMap</code>а <code>kube-proxy</code>:<br />
<pre><br />
kubectl -n kube-system edit Configmaps kube-proxy<br />
</pre><br />
<br />
* Измение в элементе <code>data.config.conf</code> значение переменной <code>conntrack.maxPerCore</code> с <code>null</code> на <code>0</code>.<br />
<br />
* Выйдите из редактора<br />
<br />
=== Запуск ControlPlane узла на rootless-узле в rootfull кластере ===<br />
<br />
Для запуска <code>ControlPlane</code> на <code>rootless-узле</code> в <code>rootfull кластере</code> на <code>ControlPlane</code> узле:<br />
<br />
* Наберите команду редактирования <code>ConfigMap</code>а <code>kubeadm-config</code>:<br />
<pre><br />
kubectl -n kube-system edit Configmaps kubeadm-config<br />
</pre><br />
<br />
* Измение в элементе <code>data.ClusterConfiguration</code> значение переменной <code>etcd.local.dataDir</code> с <code>/var/lib/etcd</code> на <code>/var/lib/podsec/u7s/etcd</code>.<br />
<br />
* Выйдите из редактора<br />
<br />
== Получениe строки подключения узла к кластеру ==<br />
<br />
=== Получении строки подключения Worker узла к кластеру ===<br />
<br />
В случае, если команда строки подключения утеряна или срок сгенерированного сертификата<br />
истек можно сгенерировать новую строку подключения, выполнив команду:<br />
<pre><br />
joinCommand=$(/usr/bin/kubeadm token create --print-join-command)<br />
</pre><br />
и выполнить команду подключения:<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
$joinCommand <br />
</pre><br />
<br />
=== Получении строки подключения Control-plane узла к кластеру ===<br />
<br />
В определенных случаях `kubeadm init` генерирует только строку подключения `worker` узлов.<br />
Или срок действия сертификата для подключения истек.<br />
<br />
В этом случае есть смысл перегенерировать сертификат:<br />
<pre><br />
cert=$(/usr/bin/kubeadm init phase upload-certs --upload-certs 2>/dev/null | tail -1)<br />
</pre><br />
строку подключения `control-plane` и `worker` узлов к кластеру:<br />
<pre><br />
joinCommand=$(/usr/bin/kubeadm token create --print-join-command)<br />
</pre><br />
и выполнить команду подключения:<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
$joinCommand --control-plane --certificate-key $cert<br />
</pre><br />
<br />
См. [https://stackoverflow.com/questions/51126164/how-do-i-find-the-join-command-for-kubeadm-on-the-master How do I find the join command for kubeadm on the master?]<br />
<br />
== Системный пользователь u7s-admin ==<br />
<br />
Все контейнеры в <code>rootless kubernetes</code>. включая системные работают от имени системного пользователя <code>u7s-admin</code>.<br />
Вы можете для мониторинга работы системы или запуска дополнительного функционала работать в системе от имени этого пользователя.<br />
<br />
Для входа в терминальный режим этого пользователя достаточно в пользователе с правами <code>root</code> набрать команду:<br />
<pre><br />
# machinectl shell u7s-admin@ /bin/bash<br />
</pre><br />
или задав пароль пользователя:<br />
<pre><br />
# passwd u7s-admin<br />
</pre> <br />
зайти в него через <code>ssh</code>.<br />
<br />
Для входа в namespace пользователя наберите команду :<br />
<pre><br />
$ nsenter_u7s<br />
#<br />
</pre><br />
<br />
В рамках своего <code>namespace</code> пользователь <code>u7s-admin</code> имеет права <code>root</code>, оставаясь в рамках системы <br />
с правам пользователя <code>u7s-admin</code>.<br />
<br />
Наличие прав <code>root</code> позволает использовать системные команды,требующих <code>root-привелегий</code> для работы с сетевым, файловым окружением (эти окружения отличаются от системных): <code>ip</code>, <code>iptables</code>, <code>crictl</code>, ...<br />
<br />
С помощью команды <code>crict</code>l можно <br />
* посмотреть наличие образов в системном кэше, <br />
* удалить, загрузить образы<br />
* посмотреть состояние контейнеров, pod'ов<br />
* и т.п. <br />
<br />
Кроме этого <code>namespace</code> пользователя <code>u7s-admin</code> присутствуют файлы и каталоге созданные в рамках данного <br />
<code>namespace</code> и отсутствующие в основной системе.<br />
Например Вы можете посмотреть логи контейнеров в каталоге <code>/var/log/pods</code> и т.п.<br />
<br />
== Особенности разворачивания приложений в rootless kubernetes ==<br />
<br />
При использовании сервисов типа <code>NodePort</code> поднятые в рамках кластера порты в диапазоне <code>30000-32767</code> остаются в <code>namespace</code> пользователя <code>u7s-admin</code>. Для их проброса наружу необходимо в пользователе <code>u7s-admin</code> запустить команду:<br />
<pre><br />
$ nsenter_u7s rootlessctl add-ports 0.0.0.0:&lt;port>:&lt;port>/tcp<br />
</pre><br />
Сервисы типа <code>NodePort</code> из за их небольшого диапазона и "нестабильности" портов при переносе решения в другой кластер довольно редко используются. Рекомендуется вместо них использовать сервисы типа <code>ClusterIP</code> c доступом к ним через <code>Ingress</code>-контроллеры.<br />
<br />
= Разворачивание rootless kubernetes кластера с балансировщиком REST-запросов haproxy =<br />
<br />
Вышеописанный процесс разворачивания обеспечивать только ручную балансировку запросов:<br />
[[Файл:Variant1.drawio.png|840px||center|rootless kubernetes-кластер без балансировщика haproxy]]<br />
<br />
Ручная балансировка запросов к <code>API-интерфейсам</code> <code>master-узлов</code> путем указания у клиентов адресов различных <br />
<code>master-узлов</code> довольно неудобна, так как не обеспечивает равномерного распределения запросов по узлам кластера и не обеспечивает автоматической отказоустойчивости при выходе из строя <code>master-узлов</code>.<br />
<br />
Решает данную проблему установка балансировщика нагрузки <code>haproxy</code>. <br />
[[Файл:Variant_haproxy_1.drawio.png|840px||center|rootless kubernetes-кластер без балансировщика haproxy]]<br />
<br />
Перевод кластера в режим балансировки запросов через haproxy возможен.<br />
Подробности описаны в статье [https://stackoverflow.com/questions/65505137/how-to-convert-a-kubernetes-non-ha-control-plane-into-an-ha-control-plane How to convert a Kubernetes non-HA control plane into an HA control plane?], но данная процедура не гарантирует корректный перевод на всех версиях <code>kubernetes</code> и ее не рекомендуют применять на <code>production</code> кластерах.<br />
<br />
Так что наиболее надежным способом создания кластера с балансировкой запросов является создание нового кластера. <br />
<br />
== Настройка балансировщика REST-запросов haproxy ==<br />
<br />
Балансировщик <code>REST-запросов haproxy</code> можно устанавливать как на отдельный сервер, так на один из серверов кластера.<br />
[[Файл:Variant haproxy master.drawio.png|840px|center|безрамки]]<br />
<br />
<blockquote><br />
'''Если балансировщик устанавливается на <code>rootless</code> сервер кластера, то для балансировщика необходимо выделить отдельный IP-адрес. Если на этом же сервере функционируют локальный регистратор (<code>registry.local</code>) и сервер подписей (<code>sigstore.local</code>), то IP-адрес балансировщика может совпадать c IP-адресами этих сервисов. <br />
''' <br />
</blockquote><br />
<br />
<blockquote><br />
'''Если планируется создание отказоустойчивого решения на основе нескольких серверов <code>haproxy</code>, то для них кроме собственного <code>IP-адреса</code> необходимо будет для всех серверов <code>haproxy</code> выделить один общий <code>IP-адрес</code>, который будет иметь <code>master-балансировщик</code>.<br />
''' <br />
</blockquote><br />
<br />
<br />
Полная настройка отказоустойчивого кластера <code>haproxy</code> из 3-х узлов описана в документе [https://www.altlinux.org/ALT_Container_OS_%D0%BF%D0%BE%D0%B4%D0%B2%D0%B5%D1%82%D0%BA%D0%B0_K8S._%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_HA_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B0 ALT Container OS подветка K8S. Создание HA кластера].<br />
<br />
Здесь же мы рассмотрим создание и настройка с одним сервером <code>haproxy</code> с балансировкой запросов на <code>master</code>-узлы.<br />
<br />
Установите пакет <code>haproxy</code>:<br />
<br />
<pre># apt-get install haproxy</pre><br />
Отредактируйте конфигурационный файл <code>/etc/haproxy/haproxy.cfg</code>:<br />
<br />
<ul><br />
<li>добавьте в него описание <code>frontend</code>’a <code>main</code>, принимающего запросы по порту <code>8443</code>:<br />
<pre><br />
frontend main<br />
bind *:8443<br />
mode tcp<br />
option tcplog<br />
default_backend apiserver<br />
</pre></li><br />
<li>добавьте описание <code>backend</code>’а <code>apiserver</code>:<br />
<pre><br />
backend apiserver<br />
option httpchk GET /healthz<br />
http-check expect status 200<br />
mode tcp<br />
option ssl-hello-chk<br />
balance roundrobin<br />
server master01 &lt;IP_или_DNS_начального_мастер_узла>:6443 check<br />
</pre></li><br />
<li>запустите <code>haproxy</code>:</li></ul><br />
<br />
<pre># systemctl enable haproxy<br />
# systemctl start haproxy</pre><br />
<br />
== Инициализация master-узла ==<br />
<br />
==== Инициализация мастер-узла при работа с балансировщиков haproxy ====<br />
<br />
При установке начального master-узла необходимо параметром <code>control-plane-endpoint</code> указать URL балансировщика <code>haproxy</code>:<br />
<br />
<pre># kubeadm init --apiserver-advertise-address 192.168.122.80 --control-plane-endpoint &lt;IP_адрес_haproxy&gt;:8443</pre><br />
При запуске в параметре <code>--apiserver-advertise-address</code> укажите IP-адрес API-интерфейса <code>kube-apiserver</code>.<br />
<br />
'''IP-адреса в параметрах''' <code>--apiserver-advertise-address</code> '''и''' <code>--control-plane-endpoint</code> '''должны отличаться. Если Вы развернули''' <code>haproxy</code> '''на том же мастер-узле, поднимите на сетевом нтерфейсе дополнительный IP-адрес и укажите его в параметре''' <code>--control-plane-endpoint</code>'''.<br />
<br />
В результате инициализации <code>kubeadm</code> выведет команды подключения дополнительных <code>control-plane</code> и <code>worker</code> узлов:<br />
<pre><br />
...<br />
You can now join any number of the control-plane node running the following command on each as root:<br />
<br />
kubeadm join &lt;IP_адрес_haproxy>:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:... \<br />
--control-plane --certificate-key ...<br />
<br />
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!<br />
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use<br />
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.<br />
<br />
Then you can join any number of worker nodes by running the following on each as root:<br />
<br />
kubeadm join <IP_адрес_haproxy>:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:...<br />
...<br />
</pre><br />
Обратите внимание - в командах присоединения узлов указывается не URL созданного начального master-узла (<code>&lt;IP_или_DNS_начального_мастер_узла&gt;:6443</code>), а URL <code>haproxy</code>.<br />
<br />
В сформированных файлах конфигурации <code>/etc/kubernetes/admin.conf</code>, <code>~/.kube/config</code> также указывается URL <code>haproxy</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://&lt;IP_адрес_haproxy>:8443<br />
</pre><br />
То есть вся работа с кластеров в дальнейшем идет через балансировщик запросов <code>haproxy</code>.<br />
<br />
Для перевода узла в состояние <code>Ready</code>, запуска coredns Pod’ов запустите flannel<br />
<br />
==== Запуск сетевого маршрутизатора для контейнеров kube-flannel ====<br />
<br />
На <code>master-узле</code> под пользоваталем <code>root</code> выполните команду:<br />
<br />
<pre># kubectl apply -f /etc/kubernetes/manifests/kube-flannel.yml<br />
Connected to the local host. Press ^] three times within 1s to exit session.<br />
[INFO] Entering RootlessKit namespaces: OK<br />
namespace/kube-flannel created<br />
clusterrole.rbac.authorization.k8s.io/flannel created<br />
clusterrolebinding.rbac.authorization.k8s.io/flannel created<br />
serviceaccount/flannel created<br />
configmap/kube-flannel-cfg created<br />
daemonset.apps/kube-flannel-ds created<br />
Connection to the local host terminated.</pre><br />
После завершения скрипта в течении минуты настраиваются сервисы мастер-узла кластера. По ее истечении проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
== Подключение дополнительных master-узлов ==<br />
<br />
<br />
=== Установка тропы PATH поиска исполняемых команд ===<br />
<br />
Измените переменную <code>PATH</code>:<br />
<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
</pre><br />
<br />
=== Подключение master (control plane) узла ===<br />
<br />
Скопируйте строку подключения <code>control-plane</code> узла и вызовите ее:<br />
<br />
<pre># kubeadm join &lt;IP_адрес_haproxy&gt;:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:... \<br />
--control-plane --certificate-key ...</pre><br />
В результате работы команда kubeadm выведет строки:<br />
<pre><br />
This node has joined the cluster and a new control plane instance was created:<br />
<br />
* Certificate signing request was sent to apiserver and approval was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
* Control plane label and taint were applied to the new node.<br />
* The Kubernetes control plane instances scaled up.<br />
* A new etcd member was added to the local/stacked etcd cluster.<br />
...<br />
Run 'kubectl get nodes' to see this node join the cluster.<br />
</pre><br />
Наберите на вновь созданном (или начальном)<code>control-plane</code> узле команду:<br />
<br />
<pre># kubectl get nodes<br />
NAME STATUS ROLES AGE VERSION<br />
&lt;host1&gt; Ready control-plane 4m31s v1.26.3<br />
&lt;host2&gt; Ready control-plane 26s v1.26.3<br />
</pre><br />
Обратите внимание, что роль (ROLES) обоих узлов - <code>control-plane</code>.<br />
<br />
Наберите команду:<br />
<pre><br />
# kubectl get all -A<br />
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
kube-flannel pod/kube-flannel-ds-2mhqg 1/1 Running 0 153m 10.96.0.1 <host1> <none> <none><br />
kube-flannel pod/kube-flannel-ds-95ht2 1/1 Running 0 153m 10.96.122.68 <host2> <none> <none><br />
...<br />
kube-system pod/etcd-<host1> 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/etcd-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-apiserver-<host1> 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-apiserver-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-controller-manager-<host1> 1/1 Running 1 (170m ago) 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-controller-manager-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-proxy-9nbxz 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-proxy-bnmd7 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-scheduler-<host1> 1/1 Running 1 (170m ago) 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-scheduler-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
...<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR<br />
kube-flannel daemonset.apps/kube-flannel-ds 2 2 2 3 3 <none> 153m kube-flannel registry.local/k8s-c10f1/flannel:v0.19.2 app=flannel<br />
kube-system daemonset.apps/kube-proxy 2 2 2 2 2 kubernetes.io/os=linux 174m kube-proxy registry.local/k8s-c10f1/kube-proxy:v1.26.3 k8s-app=kube-proxy<br />
...<br />
</pre><br />
Убедитесь, что сервисы <code>pod/etcd</code>, <code>kube-apiserver</code>, <code>kube-controller-manager</code>, <code>kube-scheduler</code>, <code>kube-proxy</code>, <code>kube-flannel</code> запустились на обоих control-plane узлах.<br />
<br />
=== Добавление master-узла в балансироващик haproxy ===<br />
<br />
Для балансировки запросов по двум серверам добавьте URL подключенного <code>control-plane</code> узла в файл конфигурации <code>/etc/haproxy/haproxy.cfg</code>:<br />
<pre><br />
backend apiserver<br />
option httpchk GET /healthz<br />
http-check expect status 200<br />
mode tcp<br />
option ssl-hello-chk<br />
balance roundrobin<br />
server master01 &lt;IP_или_DNS_начального_мастер_узла>:6443 check<br />
server master02 &lt;IP_или_DNS_подключенного_мастер_узла>:6443 check<br />
</pre><br />
и перезапустите <code>haproxy</code>:<br />
<br />
<pre># systemctl restart haproxy</pre><br />
Логи обращений и балансировку запросов между узлами можно посмотреть командой:<br />
<br />
<pre># tail -f /var/log/haproxy.log</pre><br />
<br />
== Подключение worker-узлов ==<br />
<br />
Подключение дополнительных worker-узлов происходит аналогично описанному выше в главе '''Инициализация и подключение worker-узла'''.<br />
<br />
== Настройка отказоустойчивого кластера серверов haproxy, keepalived ==<br />
<br />
=== Масштабирование haproxy, установка пакетов ===<br />
<br />
Если необходимо создать отказоустойчивое решение допускающее выход <code>haproxy</code>-севрера из строя <br />
установите <code>haproxy</code> на несколько серверов. Файлы конфигурации <code>haproxy<.code> на всех сервервх должны быть идентичны.<br />
<br />
Для контроля доступности <code>haproxy</code> и переназначений виртуального адреса дополнительно установите на каждом сервис <code>keepalived</code>:<br />
<pre><br />
# apt-get install haproxy keepalived<br />
</pre><br />
<br />
=== Конфигурирование keepalived ===<br />
<br />
[[Файл:Variant haproxy keepalived.png|840px|безрамки|центр|kubeenetes кластер с haproxy и keepalived]]<br />
<br />
Создайте файл конфигурации 'keepalived' ''/etc/keepalived/keepalived.conf'':<br />
<br />
! /etc/keepalived/keepalived.conf<br />
! Configuration File for keepalived<br />
global_defs {<br />
router_id LVS_K8S<br />
}<br />
vrrp_script check_apiserver {<br />
script "/etc/keepalived/check_apiserver.sh"<br />
interval 3<br />
weight -2<br />
fall 10<br />
rise 2<br />
}<br />
<br />
vrrp_instance VI_1 {<br />
state MASTER<br />
interface br0<br />
virtual_router_id 51<br />
priority 101<br />
authentication {<br />
auth_type PASS<br />
auth_pass 42<br />
}<br />
virtual_ipaddress {<br />
10.150.0.160 <br />
}<br />
track_script {<br />
check_apiserver<br />
}<br />
}<br />
<br />
На одном из узлов установите параметр ''state'' в значение ''MASTER'' и параметр ''priority'' в значение ''101''.<br />
На остальных параметр ''state'' в значение ''BACKUP'' и параметр ''priority'' в значение ''100''.<br />
<br />
Скрипт ''/etc/keepalived/check_apiserver.sh'' проверяет доступность балансировщика ''haproxy'':<br />
#!/bin/sh<br />
<br />
errorExit() {<br />
echo "*** $*" 1>&2<br />
exit 1<br />
}<br />
<br />
APISERVER_DEST_PORT=8443<br />
APISERVER_VIP=10.150.0.160<br />
curl --silent --max-time 2 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/"<br />
if ip addr | grep -q ${APISERVER_VIP}; then<br />
curl --silent --max-time 2 --insecure https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/"<br />
fi<br />
<br />
Параметр ''APISERVER_DEST_PORT'' задает порт балансировщиков ''haproxy'', параметр ''APISERVER_VIP'' виртуальный адрес,<br />
через который будут взаимодействовать ''master'' (''control plane'') узлы кластера ''k8s''.<br />
<br />
Скрипт проверяет работоспособность ''haproxy'' на локальной машине.<br />
<br />
Подробности см. на [[https://www.altlinux.org/Keepalived]]<br />
А если в настоящее время виртуальный адрес принадлежит текущему узлу, то и работоспособность ''haproxy'' через виртуальный адрес. <br />
<br />
Добавьте флаг на выполнение скрипта:<br />
chmod a+x /etc/keepalived/check_apiserver.sh<br />
<br />
При работающем балансировщике и хотя бы одному доступному порту ''6443'' на ''master-узлах'' скрипт<br />
должен завершаться с кодом ''0''.<br />
<br />
Подробности см. на [[Keepalived]]<br />
<br />
= Установка и настройка ingress-контролера =<br />
<br />
<code>Ingress-контроллер</code> обеспечивает переадресацию <code>http(s)</code> запросов по указанным шаблонам на внутренние сервисы <code>kubernetes-кластера</code>. <br />
Для <code>bare-metal</code> решений и решений на основе виртуальных машин наиболее приемлимым является<br />
[https://github.com/kubernetes/ingress-nginx ingress-nginx контроллер].<br />
<br />
При применении <code>Ingress-контроллера</code> нет необходимости создавать <code>Nodeport-порты</code> и пробрасывать их из <code>namespace</code> пользователя <code>u7s-admin</code>. <code>Ingress-контроллер</code> переадресует <code>http{s)</code> запрос через сервис непосредственно на порты <code>Pod</code>'ов входящих в реплики <code>deployment</code>.<br />
<br />
== Установка и настройка ingress-nginx-контролера в кластере ==<br />
<br />
[[Файл:Ingress.png|840px|безрамки|центр|Использование ingress-контроллера]]<br />
Для установки <code>Ingress-контроллера</code> скопируйте его YAML-манифест:<br />
<pre><br />
curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.0/deploy/static/provider/baremetal/deploy.yaml -o ingress-nginx-deploy.yaml<br />
</pre><br />
<br />
Выберите свободный порт в диапазона <code>30000 - 32767</code> (например <code>31000</code>) и добавьте его в элемент <br />
<code>spec.ports.appProtocol==http</code><br />
Yaml-описании <code>kind==Service</code>:<br />
<pre><br />
...<br />
---<br />
kind: Service<br />
spec:<br />
ports:<br />
- appProtocol: http<br />
...<br />
nodePort: 31000<br />
...<br />
</pre><br />
Если в Вашем решении используется ТОЛЬКО локальный регистратор <code>registry.local</code> <br />
* создайте алиасы образам nginx:<br />
<pre><br />
podman tag registry.k8s.io/ingress-nginx/controller:v1.8.0@sha256:744ae2afd433a395eeb13dc03d3313facba92e96ad71d9feaafc85925493fee3 registry.local/ingress-nginx/controller:v1.8.0<br />
podman tag registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407@sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b registry.local/ingress-nginx/kube-webhook-certgen:v20230407<br />
</pre><br />
и поместите их в локальный регистратор:<br />
<pre><br />
podman push --tls-verify=false --sign-by='<EMAIL>' registry.local/ingress-nginx/controller<br />
podman push --tls-verify=false --sign-by='<EMAIL>' registry.local/ingress-nginx/kube-webhook-certgen<br />
</pre><br />
* исправьте имена образов в скачанном нанифесте на имена образов в локальном регистраторе.<br />
<br />
Запустите Ingress-nginx-контролер:<br />
<pre><br />
kubectl apply -f ingress-nginx-deploy.yaml<br />
</pre><br />
<br />
На одном или нескольких kubernet-узлах (эти узла в дальнейшем нужно прописать в файле конфигурации балансировщика <code>haproxy</code>) пробросьте порт <code>nginx-контроллера</code> (<code>31000</code>) из <code>namespace</code> пользователя <code>u7s-admin</code> в сеть <code>kubernetes</code>:<br />
<pre><br />
nsenter_u7s rootlessctl add-ports 0.0.0.0:31000:31000/tcp<br />
</pre><br />
<br />
=== Настройка Ingress-правил ===<br />
<br />
Kubernetes поддерживает манифесты типа Ingress (kind: Ingress) описывающие правила переадресации запросов URL http-запррса на внутренние порты сервисов (kind: Service) kubernetes. Сервисы в свою очередь перенаправляют запросы на реплики Pod'ов, входящих в данный сервис.<br />
<br />
Общий вид Ingress-манифеста:<br />
<pre><br />
apiVersion: networking.k8s.io/v1<br />
kind: Ingress<br />
metadata:<br />
name: <ingress_имя><br />
spec:<br />
ingressClassName: nginx <br />
rules:<br />
- host: <домен_1><br />
http:<br />
paths:<br />
- path: /<br />
pathType: Prefix<br />
backend:<br />
service:<br />
name: <имя_сервиса_1><br />
port:<br />
number: 80<br />
- path: /<тропа_1><br />
pathType: Prefix<br />
backend:<br />
service:<br />
name: <имя_сервиса_2><br />
port:<br />
number: 80<br />
- host: <домен_2><br />
...<br />
</pre><br />
Где:<br />
* <code>host: <домен_1></code>, <code><домен_2></code>, ... - домены WEB-серверов на которых приходит запрос; <br />
* <code>path:/></code>, <code>path:/<тропа_1></code> - тропы (префиксы запросов после домена) <br />
* <code>pathType: Prefix</code> - тип троп: <code>Prefix</code> или <code>Exact</code>;<br />
* <code>service:</code> - имя сервиса на который перенаправляется запрос, если полученный запрос соответсвует правилу;<br />
* <code>port</code> - номер порта на который перенаправляется запрос.<br />
<br />
Если запросу соответствует несколько правил, выбирается правило с наиболее длинным префиксом.<br />
<br />
Подробности смотри в [https://kubernetes.io/docs/concepts/services-networking/ingress/ Kubernetes: Ingress]<br />
<br />
== Настройка haproxy и DNS ==<br />
<br />
Добавьте в файлы конфигурации <code>haproxy</code> <code>/etc/haproxy/haproxy.conf</code> переадресацию запросов на порт <code>80</code> (<code>http</code>) по IP-адресу балансировщика haproxy на IP-адреса <code>kubernet-узлов</code> на которых выбранный порт <code>nginx-контроллера</code> (<code>31000</code>) проброшен из <code>namespace</code> пользователя <code>u7s-admin</code> в сеть <code>kubernetes</code>:<br />
<pre><br />
frontend http<br />
bind *:80<br />
mode tcp<br />
option tcplog<br />
default_backend http<br />
<br />
backend http<br />
mode tcp<br />
balance roundrobin<br />
server <server1> <ip1>:31000 check<br />
server <server2> <ip2>:31000 check<br />
</pre><br />
<br />
Заведите DNS-запись связывающую DNS-имя http-сервиса с IP-адресам <code>haproxy</code>-сервера.<br />
<br />
= Выбор версии kubernetes, имени регистратора и платформы =<br />
<br />
Во время разворачивания узла командами<br />
<pre><br />
kubeadm init <br />
kubeadm join ...<br />
</pre><br />
или при создании архива образов командой <br />
<pre><br />
podsec-k8s-save-oci ...<br />
</pre><br />
есть возможность установкой переменных среды указать версию kubernetes, имя регистратора и платформы:<br />
* U7S_KUBEVERSION - версия kubernetes (v1.26.9, v1.27.7, ...);<br />
* U7S_REGISTRY - имя регистратора (registry.k8s.io, registry.altlinux.org, registry.local);<br />
* U7S_PLATFORM - имя платформы (k8s-c10f1 , k8s-p10, ...)<br />
<br />
== Выбор версии kubernetes ==<br />
<br />
Начиная с версии <code>1.0.9</code> поддерживается возможность выбора устанавливаемой версии <code>kubernetes</code>.<br />
<br />
=== Указании версии основных kubernetes образов ===<br />
<br />
Основные kubernetes-образы загружаются в момент инициализации узла командой <code>kubeadm</code>.<br />
В список основных образов входят:<br />
<pre><br />
kube-apiserver:<версия_kubernetes><br />
kube-controller-manager:<версия_kubernetes><br />
kube-scheduler:<версия_kubernetes><br />
kube-proxy:<версия_kubernetes><br />
pause:<версия__образа_pause><br />
etcd:<версия__образа_etcd><br />
coredns:<версия__образа_coredns><br />
</pre><br />
<br />
Тег образов <code>kube-*</code> совпадает с полным номером версии kubernetes типа v1.<minor>.<patch>. Например <code>v1.26.9</code>.<br />
<br />
Теги образов <code>pause</code>, <code>etcd</code>, <code>coredns</code> "зашиты" как статические переменные в <code>kubeadm</code> и могут отличаться в разных версиях <code>kubernetes</code>.<br />
<br />
Получить список образов для текущей версии <code<kuvernetes</code> можно командой:<br />
<pre><br />
# /usr/bin/kubeadm config images list 2>/dev/null<br />
</pre><br />
Пример вывода:<br />
<pre><br />
registry.k8s.io/kube-apiserver:v1.26.10<br />
registry.k8s.io/kube-controller-manager:v1.26.10<br />
registry.k8s.io/kube-scheduler:v1.26.10<br />
registry.k8s.io/kube-proxy:v1.26.10<br />
registry.k8s.io/pause:3.9<br />
registry.k8s.io/etcd:3.5.9-0<br />
registry.k8s.io/coredns/coredns:v1.9.3<br />
</pre><br />
<br />
Выбор версии определяет переменная среды <code>U7S_KUBEVERSION</code>.<br />
<br />
На <code>1.11.2023</code> при значении <code>U7S_REGISTRY</code> <code>registry.altlinux.org</code> переменная <code>U7S_KUBEVERSION</code> может принимать следующие значения:<br />
<br />
* Mинор версия v1.26:<br />
* <code>v1.26.6</code>; <br />
* <code>v1.26.9</code>;<br />
* <code>v1.26.11</code>; <br />
<br />
* Mинор версия v1.27:<br />
* <code>v1.27.11</code>;<br />
<br />
* Mинор версия v1.28:<br />
* <code>v1.28.7</code>;<br />
<br />
Данный список относится к версиям регистратора <code>registry.altlinux.org</code>. При использовании нативного регистратора <code>registry.k8s.io</code> (пустое значение <code>export U7S_REGISTRY=</code>) можно указывать любую доступную на <code>registry.k8s.io</code> версию.<br />
<br />
Примеры:<br />
<pre><br />
export U7S_REGISTRY=registry.altlinux.org<br />
export U7S_KUBEVERSION=v1.26.11<br />
</pre><br />
<br />
<pre><br />
export U7S_REGISTRY=registry.local<br />
export U7S_KUBEVERSION=v1.27.11<br />
</pre><br />
<br />
<pre><br />
export U7S_REGISTRY=<br />
export U7S_KUBEVERSION=v1.27.5<br />
</pre><br />
<br />
<br />
По умолчанию (при отсутствии значения переменной <code>U7S_KUBEVERSION</code>) принимается максимальная версия образа <br />
<code>kube-apiserver</code>.<br />
<br />
Если номер указанной минорной версии <code>kubernetes</code> отличается от текущего, при вызове команды <code>kubeadm</code><br />
производится удаление текущих <code>rpm-пакетов</code> <code>kubernetes*</code>, <code>cri-o</code> и установка <code>rpm-пакетов</code> указанной версии.<br />
<br />
Возможна ситуация, когда на регистраторе образов отсутствует версия образа, полученная в результате выполнения команды: <br />
<pre><br />
/usr/bin/kubeadm config images list<br />
</pre><br />
Если в переменные среды добавить переменную:<br />
<pre><br />
export U7S_SETAVAILABLEIMAGES=yes<br />
</pre><br />
то в качестве стандартного образа принимается образ с максимальной версией в рамках данной минорной версии <br />
(<code>1.26</code>, <code>1.27</code>, <code>1.28</code>).<br />
Данному образу присваивается тег, полученнфй в результате выполнения команды <code> kubeadm config images</code>.<br />
<br />
=== Указании версии дополнительных kubernetes образов ===<br />
<br />
Кроме основных образов при разворачивании кластера используются дополнительные образы:<br />
* flannel:<U7S_FLANNEL_TAG>;<br />
* flannel-cni-plugin:<U7S_FLANNELCNIPLUGIN_TAG><br />
* cert-manager-webhook:<U7S_CERTMANAGER_TAG>;<br />
* cert-manager-controller:<U7S_CERTMANAGER_TAG>;<br />
* cert-manager-cainjector:<U7S_CERTMANAGER_TAG>.<br />
<br />
Если переменным среды <br />
<code>U7S_FLANNEL_TAG</code>, <br />
<code>U7S_FLANNELCNIPLUGIN_TAG</code>, <br />
<code>U7S_CERTMANAGER_TAG</code> не присвоены значения,<br />
то для каждого образа определяется максимальная версия в регистраторе <br />
и загружается найденная версия образа.<br />
<br />
При необходимости можно изменить версию образа экпортировав перед запускам команды соответствующую переменную. Например:<br />
<pre><br />
export U7S_FLANNEL_TAG=v0.19.2<br />
</pre><br />
<br />
== Выбор исходного регистратора kubernetes-образов ==<br />
<br />
Во время инициализации <code>master-узла</code> кластера (<code>kubeadm init</code>) или во время подключения узла к кластеру (<code>kubeadm join</code>) команда <code>kubeadm</code> может загружать образы с различных регистраторов образов и с различными префиксами.<br />
<br />
Выбор регистратора и префикса образов определяет переменная среды <code>U7S_REGISTRY</code>. <br />
Если переменная не задана регистратор префикс определяется автоматически на основе конфигурационных файлов <code>/etc/os-release</code> и <code>/etc/hosts</code>. <br />
<br />
Переменная среды <code>U7S_REGISTRY</code> может принимать следующие основные значения:<br />
* пустое значение;<br />
* <code>registry.altlinux.org</code>;<br />
* <code>registry.local</code>;<br />
* ...<br />
<br />
=== Нативные kubernetes-образы ===<br />
<br />
<pre><br />
export U7S_REGISTRY=<br />
</pre><br />
Если переменная <code>U7S_REGISTRY</code> установлена в пустое значение образы загружаются со стандартного регистратора образов <code>kubernetes</code>.<br />
<br />
=== Образы altlinux ===<br />
<br />
==== Регистратор registry.altlinux.org ====<br />
<br />
<pre><br />
export U7S_REGISTRY=registry.altlinux.org<br />
</pre><br />
С регистратора <code>altlinux</code> устанавливаются образы при наличии доступа в Интернет.<br />
<br />
==== Локальный регистратор ====<br />
<br />
<pre><br />
export U7S_REGISTRY=registry.local<br />
</pre><br />
<br />
Локальный регистратор используется в сертифицированных дистрибутивах, которые содержат kubernetes-образы на установочном диске.<br />
<br />
Локальный регистратор образов <code>registry.local</code> может обеспечивать:<br />
* разворачивание кластера без доступа в Интернет;<br />
* ускоренное разворачивание как кластера, так и проектов, разворачиваемых в его рамках, так как образы необходимые для запуска <code>Pod</code>'ов загружаются по локальной сети; <br />
* высокий уровень защищенности системы путем установки политик разрешающих загрузку только подписанных образов и только с локального регистратора <code>registry.local</code>.<br />
<br />
Пакет <code>podsec</code> обеспечивает:<br />
* Установку на рабочих местах клиентов и узлах <code>kubernetes</code> политик доступа к образом для различных категория пользователей (скрипт <code>podsec-create-policy</code>).<br />
* Разворачивание на одном узлов локального регистратора образов и сервера подписей образов (скрипт <code>podsec-create-services</code>). <br />
* Загрузку с регистратора <code>registry.altlinux.org</code> образов необходимых для разворачивания <code>kubernetes</code> и формирования максимально сжатого (<200Mb) архива. (скрипты <code>podsec-k8s-save-oci</code>, <code>podsec-save-oci</code>)<br />
* разворачивание образов из архива, их подпись размещение на локальном регистраторе (скрипт <code>podsec-load-sign-oci</code>). <br />
<br />
В зависимости от значения переменных <code>U7S_REGISTRY</code>, <code>U7S_PLATFORM</code>, <code>U7S_KUBEVERSION</code> скрипт <code>podsec-k8s-save-oci</code> формирует архив образов различных версий kubernetes:<br />
* <code>registry.local/k8s-c10f1</code> - архив образов для сертифицированного дистрибутива <code>c10</code> на основе набора образов с регистратора <code>registry.altlinux.org</code> с платформой <code>k8s-c10f1</code>;<br />
* <code>registry.local/k8s-p10</code> - архив образов для несертифицированного дистрибутива <code>p10</code> на основе набора образов с регистратора <code>registry.altlinux.org</code> с платформой <code>k8s-p10</code>;<br />
<br />
Локальный регистратор <code>registry.local</code> может также хранить подписанные образы и запускаемых в рамках кластера проектов. Необходимо только, чтобы каждый образ в рамках локального регистратор <code>registry.local</code> имел префикс. Образы типа <code>registry.local/<имя_образа></code> не допускаются, так как для них трудно определить "подписанта" образа.<br />
<!--<br />
* <code>export U7S_REGISTRY=registry.local/k8s-c10f1</code> - образы для сертифицированного дистрибутива <code>c10</code>;<br />
* <code>export U7S_REGISTRY=registry.local/k8s-p10</code> - образы для несертифицированного дистрибутива <code>p10</code>.<br />
--><br />
<br />
===== podsec-create-policy - настройка политики доступа к образам различным категориям пользователей =====<br />
<br />
'''Формат''':<br />
<pre>podsec-create-policy <ip-адрес_регистратора_и_сервера_подписей></pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт <code>podsec-create-policy</code> формирует в файлах <code>/etc/containers/policy.json</code>, <br />
<code>/etc/containers/registries.d/default.yaml</code> максимально защищенную политику доступа к образам - по умолчанию допускается доступ только к подписанным образам локального регистратора <code>registry.local</code>.<br />
Данная политика распространяется как на пользователей имеющих права суперпользователя, так и на пользователей группы <code>podsec</code>, создаваемые podsec-скриптом <code>podsec-create-podmanusers</code>. <br />
<br />
Пользователи группы <code>podsec-dev</code>, создаваемые podsec-скриптом <code>podsec-create-imagemakeruser</code> имеют неограниченные права на доступ, формирования образов, их подпись и помещение на локальный регистратор <code>registry.local</code>.<br />
<br />
В разворачиваниях kubernetes не требующих таких жестких ограничений в политике доступа и работы с образами политики могут быть смягчены путем модифицирования cистемных файлов политик <code>/etc/containers/policy.json</code>, <code>/etc/containers/registries.d/default.yaml</code> или файлов установки политик пользователей <code>~/.config/containers/policy.json</code>, <code>~/.config/containers/registries.d/default.yaml</code>.<br />
<br />
===== podsec-create-services - разворачивание локального регистратора образов и сервера подписей образов =====<br />
<br />
Скрипт <code>podsec-create-services</code> обеспечивает разворачивание локального регистратора образов и сервера подписей образов.<br />
<br />
===== Поддержка электронной подписи образов =====<br />
<br />
Для <code>kubernetes-образов</code>, хранящихся в архиве образов распаковку образов, их подпись и размещение на локальном регистраторе <code>registry.local</code> обеспечивает скрипт <code>podsec-load-sign-oci</code> запускаемый пользователем группы <code>podsec-dev</code>.<br />
<br />
Для других образов пользователь группы <code>podsec-dev</code> должен создать образ в домене локального регистратора <code>registry.local/</prefix>/</code> и поместить его в регистратор командой:<br />
<pre>podman push --tls-verify=false --sign-by="<email-подписанта" <образ></pre><br />
<br />
Образ в домене <code>registry.local/</prefix>/</code> может быть получен:<br />
* присваивании алиаса стороннему образу:<br />
<pre>podman tag <сторонний_образ> registry.local/</prefix>/<локальный_образ></pre><br />
* сборки образов через <code>Dockerfile</code>.<br />
<pre>podman build -t registry.local/</prefix>/<локальный_образ> ...</pre><br />
<br />
== Указание платформы ==<br />
<br />
Кроме имени регистратора kubernetes-образы altlinux содержит в имени (например registry.altlinux.org/k8s-p10/kube-apiserver) название платформы:<br />
* k8s-p10 - образы для дистрибутива p10;<br />
* k8s-c10f1 - образы сертифицированного дистрибутива c10;<br />
* test_k8s - тестовые образы;<br />
* ...<br />
<br />
Платформу устанавливаемых образов можно указать в переменной <code>U7S_PLATFORM</code>. Например:<br />
<pre><br />
export U7S_PLATFORM=test_k8s<br />
</pre><br />
<br />
== Автоматический выбор регистратора образов и платформы ==<br />
<br />
Если переменная <code>U7S_REGISTRY</code> не установлена, ее значения вычисляется автоматически по следующему алгоритму:<br />
<br />
* Если файл <code>/etc/hosts</code> содержит описание хоста <code>registry.local</code> префикс переменной <code>U7S_REGISTRY</code> принимает значение <code>registry.local/</code>, иначе <code>registry.altlinux.org/</code>.<br />
* Если переменная <code>CPE_NAME</code> файла <code>/etc/os-release</code> содержит значение <code>spserver</code> суффикс переменной <code>U7S_PLATFORM</code> принимает значение <code>k8s-c10f1</code>, иначе <code>k8s-p10</code>.<br />
<br />
= podsec-k8s-rbac - Поддержка управление доступом на основе ролей (RBAC) =<br />
<br />
В пакет <code>podsec-k8s-rbac</code> входит набор скриптов для работы с <code>RBAC</code> - <code>Role Based Access Control</code>:<br />
* <code>podsec-k8s-rbac-create-user</code> - создание <code>RBAC-пользователя</code>;<br />
* <code>podsec-k8s-rbac-create-kubeconfig</code> - создание ключей, сертификатов и файла конфигурации <code>RBAC-пользователя</code>;<br />
* <code>podsec-k8s-rbac-create-remoteplace</code> - создание удаленного рабочего места;<br />
* <code>podsec-k8s-rbac-bindrole</code> - привязывание пользователя к кластерной или обычной роли;<br />
* <code>podsec-k8s-rbac-get-userroles</code> - получить список кластерные и обычных ролей пользователя;<br />
* <code>podsec-k8s-rbac-unbindrole</code> - отвязывание пользователя от кластерной или обычной роли.<br />
<br />
== podsec-k8s-rbac-create-user - создание RBAC-пользователя ==<br />
<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-user имя_пользователя</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт:<br />
* создает RBAC пользователя<br />
* создает в домашнем директории каталог .kube<br />
* устанавливаются соответствующие права доступа к каталогам.<br />
<br />
== podsec-k8s-rbac-create-kubeconfig - создание ключей, сертификатов и файла конфигурации RBAC-пользователя ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-kubeconfig имя_пользователя[@<имя_удаленного_пользователя>] [группа ...]</pre><br />
<br />
'''Описание''':<br />
Скрипт должен вызываться администратором безопасности средства контейнеризации.<br />
<br />
Для <code>rootless</code> решения имя удаленного пользователя принимается <code>u7s-admin</code>.<br />
<br />
Для <code>rootfull</code> решения необходимо после символа <code>@</code> указать <code>имя удаленного пользователя</code>.<br />
<br />
Скрипт в каталоге <code>~имя_пользователя/.kube производит</code>:<br />
* Создании личного (private) ключа пользователя (файл <code>имя_пользователя.key</code>).<br />
* Создание запроса на подпись сертификата (CSR) (файл <code>имя_пользователя.key</code>).<br />
* Запись <code>запроса на подпись сертификата CSR </code>в кластер.<br />
* Подтверждение <code>запроса на подпись сертификата (CSR)</code>.<br />
* Создание <code>сертификата</code> (файл <code>имя_пользователя.crt</code>).<br />
* Проверку корректности сертификата<br />
* Формирование файла конфигурации пользователя (файл <code>config</code>)<br />
* Добавление контекста созданного пользователя<br />
<br />
== podsec-k8s-rbac-create-remoteplace - создание удаленного рабочего места ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-remoteplace ip-адрес</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит настройку удаленного рабочего места пользователя путем копирования его конфигурационного файла.<br />
<br />
== podsec-k8s-rbac-bindrole - привязывание пользователя к кластерной или обычной роли ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-bindrole имя_пользователя role|role=clusterrole|clusterrole роль имя_связки_роли [namespace]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит привязку пользователя к обычной или кластерной роли используя имя_связки_роли.<br />
<br />
'''Параметры''':<br />
<br />
* имя_пользователя должно быть создано командой podsec-k8s-rbac-create-user и сконфигурировано на доступ к кластеру командой podsec-k8s-rbac-create-kubeconfig;<br />
* тип роли может принимать следующие значения:<br />
* role - пользователь привязывется к обычной роли с именем <роль> (параметр namespace в этом случае обязателен);<br />
* role=clusterrole - пользователь привязывется к обычной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае обязателен);<br />
* clusterrole - пользователь привязывется к кластерной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае должен отсутствовать).<br />
* роль - имя обычной или кластерной роли в зависимости от предыдущего параметра;<br />
* имя_связки_роли - имя объекта класса rolebindings или clusterrolebindings в зависимости от параметра тип роли. В рамках этого объекта к кластерной или обычной роли могут быть привязаны несколько пользователей.<br />
* namespace - имя namespace для обычной роли.<br />
<br />
== podsec-k8s-rbac-get-userroles - получить список кластерные и обычных ролей пользователя ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-get-userroles имя_пользователя [showRules]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт формирует список кластерные и обычных ролей которые связаны с пользователем. При указании флага <code>showRules</code>, для каждой роли указывается список правил ("<code>rules:[...]</code>"), которые принадлежат каждой роли пользователя.<br />
<br />
Результат возвращается в виде <code>json-строки</code> формата:<br />
<pre><br />
{<br />
"": {<br />
"clusterRoles": [...],<br />
"roles": {<br />
"allNamespaces": [...],<br />
"namespaces": [<br />
{<br />
"": [...],<br />
...<br />
}<br />
}<br />
}<br />
}<br />
</pre><br />
Где <code>[...]</code> - массив объектов типа:<br />
<pre><br />
{<br />
"bindRoleName": "",<br />
"bindedRoleType": "ClusterRole|Role",<br />
"bindedRoleName": "",<br />
"unbindCmd": "podsec-k8s-rbac-unbindrole ..."<br />
}<br />
</pre><br />
<br />
== podsec-k8s-rbac-unbindrole - отвязывание пользователя от кластерной или обычной роли ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-unbindrole имя_пользователя role|clusterrole роль имя_связки_роли [namespace]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит отвязку роли от кластерной или обычной роли, созданной командой <code>podsec-k8s-rbac-bindrole</code>. Полный текст команды можно получить в выводе команды <code>podsec-k8s-rbac-get-userroles</code> в поле <code>unbindCmd</code>. Если в указанном имя_связки_роли объекте класса <code>rolebindings</code> или <code>clusterrolebindings</code> еще остаются пользователи - объект модифицируется. Если список становится пуст - объект удаляется.<br />
<br />
'''Параметры''':<br />
<br />
* <code>имя_пользователя</code> должно быть создано командой <code>podsec-k8s-rbac-create-user</code> и сконфигурировано на доступ к кластеру командой <code>podsec-k8s-rbac-create-kubeconfig</code>;<br />
* тип роли может принимать следующие значения:<br />
* <code>role</code> - пользователь привязывается к обычной роли с именем <роль> (параметр <code>namespace</code> в этом случае обязателен);<br />
* <code>clusterrole</code> - пользователь привязывается к кластерной роли используя кластерную роль с именем <роль> (параметр <code>namespace</code> в этом случае должен отсутствовать).<br />
* <code>роль</code> - имя обычной или кластерной роли в зависимости от предыдущего параметра;<br />
* <code>имя_связки_роли</code> - имя объекта класса <code>rolebindings</code> или <code>clusterrolebindings</code> в зависимости от параметра тип роли. В рамках этого объекта к кластерной или обычной роли могут быть привязаны несколько пользователей.<br />
* <code>namespace</code> - имя <code>namespace</code> для обычной роли.<br />
<br />
= podsec-inotify - Мониторинг безопасности системы =<br />
<br />
В пакет podsec-inotify входит набор скриптов для мониторинга безопасности системы: <br />
* podsec-inotify-check-policy - проверка настроек политики контейнеризации на узле;<br />
* podsec-inotify-check-containers - проверка наличия изменений файлов в директориях rootless контейнерах;<br />
* podsec-inotify-check-images - проверка образов на предмет их соответствия настройки политикам контейнеризации на узле;<br />
* podsec-inotify-check-kubeapi - мониторинг аудита API-интерфейса kube-apiserver control-plane узла;<br />
* podsec-inotify-check-vuln - мониторинг docker-образов узла сканером безопасности trivy.<br />
<br />
== podsec-inotify-check-policy - проверка настроек политики контейнеризации на узле ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-policy [-v[vv]] [-a интервал] [-f интервал] -c интервал -h интервал [-m интервал] х-w интервалъ [-l интервал] [-d интервал]</pre><br />
<br />
'''Описание''':<br />
Плугин проверяет настройки политики контейнеризации на узле.<br />
<br />
Проверка идет по следующим параметрам:<br />
<br />
* файл <code>policy.json</code> установки транспортов и политик доступа к регистраторам:<br />
{| class="wikitable"<br />
|+<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| имеющих <code>defaultPolicy != reject</code>, но не входящих в группу <code>podman_dev</code> || 102<br />
|-<br />
| не имеющих не имеющих <code>registry.local</code> в списке регистраторов для которых проверяется наличие электронной подписи образов || 103<br />
|-<br />
| имеющих в политике регистраторы для которых не проверяется наличие электронной подписи образов || 104<br />
|-<br />
| имеющих в списке поддерживаемых транспорты отличные от <code>docker</code> (транспорт получения образов с регистратора) || 105<br />
|}<br />
<br />
<br />
* файлы привязки регистраторов к серверам хранящим электронные подписи (файл привязки о умолчанию <code>default.yaml</code> и файлы привязки регистраторов <code>*.yaml</code> каталога <code>registries.d</code>). Наличие (число) пользователей:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| не использующих хранилище подписей <code>http://sigstore.local:81/sigstore/</code> как хранилище подписей по умолчанию || 106<br />
|}<br />
<br />
* контроль групп пользователей<br />
# наличие пользователей имеющих образы, но не входящих в группу <code>podman</code>:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| наличие пользователей имеющих образы, но не входящих в группу <code>podman</code> || 101<br />
|}<br />
# наличие пользователей группы <code>podman</code> (за исключением входящих в группу <code>podman_dev</code>):<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| входящих в группу <code>wheel</code> || 101<br />
|-<br />
| имеющих каталог <code>.config/containers/</code> открытым на запись и изменения || 90 * <code>доля_нарушителей</code><br />
|-<br />
| не имеющих файла конфигурации <code>.config/containers/storage.conf</code> || 90 * <code>доля_нарушителей</code><br />
|}<br />
<code>доля_нарушителей</code> считается как: <code>число_нарушителей / число_пользователей_группы_podman</code><br />
<br />
Все веса метрик суммируются и формируется итоговая метрика.<br />
<br />
== podsec-inotify-check-containers - проверка наличия изменений файлов в директориях rootless контейнерах ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-containers</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт:<br />
* создаёт список директорий <code>rootless</code> контейнеров, существующих в системе,<br />
* запускает проверку на добавление,удаление, и изменение файлов в директориях контейнеров,<br />
* отсылает уведомление об изменении в системный лог.<br />
<br />
== podsec-inotify-check-images - проверка образов на предмет их соответствия настройки политикам контейнеризации на узле ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-images [-v[vv]] [-a интервал] [-f интервал] -c интервал -h интервал [-m интервал] х-w интервалъ [-l интервал] [-d интервал]</pre><br />
<br />
'''Описание''':<br />
<br />
Плугин проверяет образы на предмет их соответствия настройки политикам контейнеризации на узле. Проверка идет по следующим параметрам:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| наличие в политике пользователя регистраторов не поддерживающие электронную подпись || 101<br />
|-<br />
| наличие в кэше образов неподписанных образов || 101<br />
|-<br />
| наличие в кэше образов вне поддерживаемых политик || 101<br />
|}<br />
Все веса метрик суммируются и формируется итоговая метрика.<br />
<br />
== podsec-inotify-check-kubeapi - мониторинг аудита API-интерфейса kube-apiserver control-plane узла ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-kubeapi [-d]</pre><br />
<br />
'''Описание''':<br />
Скрипт производит мониторинг файла <code>/etc/kubernetes/audit/audit.log</code> аудита API-интерфейса <code>kube-apiserver</code>.<br />
<br />
Политика аудита располагается в файле <code>/etc/kubernetes/audit/policy.yaml</code>:<br />
<pre><br />
apiVersion: audit.k8s.io/v1<br />
kind: Policy<br />
omitManagedFields: true<br />
rules:<br />
# do not log requests to the following <br />
- level: None<br />
nonResourceURLs:<br />
- "/healthz*"<br />
- "/logs"<br />
- "/metrics"<br />
- "/swagger*"<br />
- "/version"<br />
- "/readyz"<br />
- "/livez"<br />
<br />
- level: None<br />
users:<br />
- system:kube-scheduler<br />
- system:kube-proxy<br />
- system:apiserver<br />
- system:kube-controller-manager<br />
- system:serviceaccount:gatekeeper-system:gatekeeper-admin<br />
<br />
- level: None<br />
userGroups:<br />
- system:nodes<br />
- system:serviceaccounts<br />
- system:masters<br />
<br />
# limit level to Metadata so token is not included in the spec/status<br />
- level: Metadata<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: authentication.k8s.io<br />
resources:<br />
- tokenreviews<br />
<br />
# extended audit of auth delegation<br />
- level: RequestResponse<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: authorization.k8s.io<br />
resources:<br />
- subjectaccessreviews<br />
<br />
# log changes to pods at RequestResponse level<br />
- level: RequestResponse<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: "" # core API group; add third-party API services and your API services if needed<br />
resources: ["pods"]<br />
verbs: ["create", "patch", "update", "delete"]<br />
<br />
# log everything else at Metadata level<br />
- level: Metadata<br />
omitStages:<br />
- RequestReceived<br />
</pre> <br />
<br />
Текущие настройки производят логирование всех обращений "несистемных" пользователей (в том числе анонимных) к ресурсам <code>kubernetes</code>.<br />
<br />
Скрипт производит выборку всех обращений, в ответ на которые был сформирован код более <code>400</code> - запрет доступа. <br />
Все эти факты записываются в системный журнал и накапливаются в файле логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code>, который периодически передается через посту системному адмиристратору.<br />
<br />
'''Параметры''':<br />
<br />
* <code>-d</code> - скирпт запускается в режиме демона, производящего онлайн мониторинг файла <code>/etc/kubernetes/audit/audit.log</code> и записывающего факты запросов с запретом доступа в системный журнал и файл логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code>.<br />
<br />
* при запуске без параметров скрипт посылает файл логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code> почтой системному администратору (пользователь <code>root</code>) и обнуляет файл логов.<br />
<br />
В состав пакета кроме этого скрипта входят:<br />
<br />
* файл описания сервиса </code>/lib/systemd/system/podsec-inotify-check-kubeapi.service</code>. Для его запуска екобходимо выполнить команды:<br />
<pre> <br />
# systemctl enable podsec-inotify-check-kubeapi.service<br />
# systemctl start podsec-inotify-check-kubeapi.service<br />
</pre><br />
<br />
* файл для </code>cron</code> </code>/etc/podsec/crontabs/podsec-inotify-check-kubeapi</code>. Файл содержит единственную строку с описанием режима запуска скрипта </code>podsec-inotify-check-kubeapi</code> для передачи почты системному администратору. <br />
Скрипт запускается один раз в 10 минут.<br />
Во время установки пакета строка файла (в случае ее отсутствия) дописыватся в </code>crontab</code>-файл </code>/var/spool/cron/root</code> пользователя </code>root</code>. <br />
Если необходимо изменить режим запуска скрипта или выключить его это можно сделать командой редактирования </code>crontab</code>-файла:<br />
<pre><br />
# crontab -e<br />
</pre><br />
<br />
== podsec-inotify-check-vuln - мониторинг docker-образов узла сканером безопасности trivy ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-vuln</pre><br />
<br />
'''Описание''':<br />
<br />
<br />
<br />
Для корректной работы скрипта необходимо запустить сервис <code>podsec-inotify-server-trivy</code>:<br />
<pre><br />
systemctl enable --now podsec-inotify-server-trivy<br />
</pre><br />
<br />
Скрипт производит мониторинг <code>docker-образов</code> узла сканером безопасности <code>trivy</code>:<br />
<br />
* Если скрипт запускается от имени пользователя <code>root</code> скрипт:<br />
# проверяет сканером <code>trivy</code> <code>rootfull</code> образы;<br />
# для всех пользователей каталога <code>/home/</code> проверяется наличие <code>rootless</code>-образов. При их наличии проверяет сканером <code>trivy</code> эти образы.<br />
<br />
* Если скрипт запускается от имени обычного пользователя проверяется наличие <code>rootless</code>-образов. При их наличии проверяет сканером <code>trivy</code> эти образы.<br />
<br />
Результат анализа посылается в системный лог.<br />
Если при анализе образа число обнаруженных угроз уровня <code>HIGH</code> больше 0, результат посылается почтой системному администратору (<code>root</code>).<br />
<br />
'''Параметры''':<br />
<br />
Отсутствуют.<br />
<br />
В состав пакета кроме этого скрипта входит файл для <code>cron</code> <code>/etc/podsec/crontabs/podsec-inotify-check-vuln</code>. Файл содержит единственную строку с описанием режима запуска скрипта <code>podsec-inotify-check-vuln</code>.<br />
Во время установки пакета строка файла (в случае ее отсутствия) дописыватся в <code>crontab</code>-файл <code>/var/spool/cron/root</code> пользователя <code>root</code>.<br />
<br />
Если необходимо изменить режим запуска скрипта или выключить его это можно сделать командой редактирования <code>crontab</code>-файла:<br />
<pre><br />
# crontab -e<br />
</pre></div>Kafhttps://www.altlinux.org/index.php?title=Rootless_kubernetes&diff=78991Rootless kubernetes2024-03-25T17:15:18Z<p>Kaf: /* Указании версии дополнительных kubernetes образов */</p>
<hr />
<div><br />
<br />
= Общее описание =<br />
<br />
Запуск <code>Kubernetes</code> в режиме <code>rootless</code> обеспечивает запуск <code>Pod</code>ов без системных <code>root</code>-привелегий в рамках <code>user namespace</code> системного пользователя <code>u7s-admin</code>. Работа в этом режиме практически не требует никаких модификаций, но обеспечивает повышенные уровень защищенности <code>kubernetes</code>, так как клиентские приложения даже при использовании уязвимостей не могут получить права пользователя <code>root</code> и нарушить работу узла.<br />
<br />
Запуск <code>kubernetes</code> версии <code>1.26.3</code> и старше в режиме <code>rootless</code> обеспечивает пакет <code>podsec-k8s</code> версии <code>1.0.5</code> или выше.<br />
<br />
'''Для разворачивания <code>rootless kubernetes</code> требуются ядра ядрах ''5.15'' и выше.'''<br />
<br />
= podsec-k8s - Быстрый старт =<br />
<br />
== Установка master-узла ==<br />
<br />
=== Инициализация master-узла ===<br />
<br />
Для запуска <code>kubernetes</code> в режиме <code>rootless</code> установите пакет <code>podsec-k8s</code> версии <code>1.0.5</code> или выше.<br />
<pre>apt-get install podsec-k8s</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
В каталоге <code>/usr/libexec/podsec/u7s/bin/</code> находятся программы, обеспечивающие работы <code>kubernetes</code><br />
в <code>rootless</code>-режиме.<br />
<br />
Для разворачивания <code>master-узла</code> запустите команду:<br />
<br />
<pre>kubeadm init</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>init</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
После:<br />
<br />
* генерации сертификатов в каталоге <code>/etc/kubernetes/pki</code>,<br />
* загрузки образов, <br />
* генерации conf-файлов в каталоге <code>/etc/kubernetes/manifests/</code>, <code>/etc/kubernetes/manifests/etcd/</code><br />
* запуска сервиса <code>kubelet</code> и <code>Pod</code>’ов системных <code>kubernetes-образов</code><br />
<br />
инициализируется <code>kubernet-кластер</code> из одного узла.<br />
<br />
По окончании скрипт выводит строки подключения <code>master</code>(<code>Control Plane</code>) и <code>worker-узлов</code>:<br />
<pre><br />
You can now join any number of control-plane nodes by copying certificate authorities<br />
and service account keys on each node and then running the following as root:<br />
<br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:.. --control-plane<br />
<br />
Then you can join any number of worker nodes by running the following on each as root:<br />
<br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...<br />
</pre><br />
<br />
=== Запуск сетевого маршрутизатора для контейенеров kube-flannel ===<br />
<br />
Для версии <code>podsec 1.0.8</code> и выше этот шаг выполнять не надо - он выполняется во время <code>kubeadm init</code>. <br />
<br />
Для перевода узла в состояние <code>Ready</code>, запуска <code>coredns</code> <code>Pod</code>’ов запустите <code>flannel</code>.<br />
<br />
На <code>master-узле</code> под пользоваталем <code>root</code> выполните команду:<br />
<br />
<pre># kubectl apply -f /etc/kubernetes/manifests/kube-flannel.yml<br />
Connected to the local host. Press ^] three times within 1s to exit session.<br />
[INFO] Entering RootlessKit namespaces: OK<br />
namespace/kube-flannel created<br />
clusterrole.rbac.authorization.k8s.io/flannel created<br />
clusterrolebinding.rbac.authorization.k8s.io/flannel created<br />
serviceaccount/flannel created<br />
configmap/kube-flannel-cfg created<br />
daemonset.apps/kube-flannel-ds created<br />
Connection to the local host terminated.</pre><br />
После завершения скрипта в течении минуты настраиваются сервисы мастер-узла кластера. По ее истечении проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
=== Проверка работы master-узла ===<br />
<br />
На <code>master-узле</code> выполните команду:<br />
<br />
<pre># kubectl get daemonsets.apps -A<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE<br />
kube-flannel kube-flannel-ds 1 1 1 1 1 &lt;none&gt; 102s<br />
kube-system kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 8h</pre><br />
Число <code>READY</code> каждого <code>daemonset</code> должно быть равно числу <code>DESIRED</code> и должно быть равно числу узлов кластера.<br />
<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host> Ready control-plane 16m v1.26.3 10.96.0.1 <none> ALT SP Server 11100-01 5.15.105-un-def-alt1 cri-o://1.26.2<br />
</pre><br />
Проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
<pre><br />
# kubectl get all -A<br />
NAMESPACE NAME READY STATUS RESTARTS AGE<br />
kube-system pod/coredns-c7df5cd6c-5pkkm 1/1 Running 0 19m<br />
kube-system pod/coredns-c7df5cd6c-cm6vf 1/1 Running 0 19m<br />
kube-system pod/etcd-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-apiserver-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-controller-manager-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-proxy-lqf9c 1/1 Running 0 19m<br />
kube-system pod/kube-scheduler-host-212 1/1 Running 0 19m<br />
<br />
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE<br />
default service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19m<br />
kube-system service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 19m<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE<br />
kube-system daemonset.apps/kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 19m<br />
<br />
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE<br />
kube-system deployment.apps/coredns 2/2 2 2 19m<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY AGE<br />
kube-system replicaset.apps/coredns-c7df5cd6c 2 2 2 19m<br />
</pre><br />
Состояние всех <code>Pod</code>’ов должны быть в <code>1/1</code>.<br />
<br />
Проверьте состояние дерева <code>rootless-процессов</code>:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─nsenter.sh───nsenter───_kubelet.sh───kubelet───11*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-controller───7*[{kube-controller}]<br />
│ │ ├─conmon───kube-apiserver───8*[{kube-apiserver}]<br />
│ │ ├─conmon───kube-scheduler───7*[{kube-scheduler}]<br />
│ │ ├─conmon───etcd───8*[{etcd}]<br />
│ │ ├─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─2*[conmon───coredns───8*[{coredns}]]<br />
│ │ ├─rootlesskit.sh───crio───10*[{crio}]<br />
│ │ └─7*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
...<br />
</pre><br />
Процесс <code>kubelet</code> запускается как сервис в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
Все остальные процессы <code>kube-controller</code>, <code>kube-apiserver</code>, <code>kube-scheduler</code>, <code>kube-proxy</code>, <code>etcd</code>, <code>coredns</code> запускаются как контейнеры от соответствующих образов в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
=== Обеспечение запуска обычных POD’ов на мастер-узле ===<br />
<br />
По умолчанию на master-узле пользовательские <code>Pod</code>ы не запускаются. Чтобы снять это ограничение наберите команду:<br />
<br />
<pre># kubectl taint nodes &lt;host&gt; node-role.kubernetes.io/control-plane:NoSchedule-<br />
node/&lt;host&gt; untainted</pre><br />
== Инициализация и подключение worker-узла ==<br />
<br />
Установите пакет <code>podsec-k8s</code>:<br />
<pre><br />
apt-get install podsec-k8s<br />
</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
<br />
=== Подключение worker-узлов ===<br />
<br />
Скопируйте команду подключения <code>worker-узла</code>, полученную на этапе установки начального <code>master-узла</code>. Запустите ее:<br />
<br />
<pre>kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>join</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
По окончании скрипт выводит текст:<br />
<pre><br />
This node has joined the cluster:<br />
* Certificate signing request was sent to apiserver and a response was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
<br />
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.<br />
</pre><br />
<br />
=== Проверка состояния процессов ===<br />
<br />
Проверьте состояние дерева <code>rootless-процессов</code>:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─nsenter.sh───nsenter───_kubelet.sh───kubelet───10*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─rootlesskit.sh───crio───9*[{crio}]<br />
│ │ └─6*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
...<br />
</pre><br />
Процесс <code>kubelet</code> запускается как сервис в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
Все остальные процессы <code>kube-proxy</code>, <code>kube-flannel</code> запускаются как контейнеры от соответствующих образов в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
=== Проверка готовности master и worker узлов kubernets ===<br />
<br />
Зайдите на <code>master-узел</code> и проверьте подключение <code>worker-узла</code>:</li></ol><br />
<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host1> Ready control-plane 7h54m v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
&lt;host2> Ready &lt;none&gt; 8m30s v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
...<br />
</pre><br />
<br />
== Инициализация и подключение дополнительных master-узлов ==<br />
<br />
Установите пакет <code>podsec-k8s</code>:<br />
<pre><br />
apt-get install podsec-k8s<br />
</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
<br />
=== Подключение master-узлов ===<br />
<br />
Скопируйте команду подключения <code>master-узла</code>, полученную на этапе установки начального <code>master-узла</code>. <br />
Она отличается от команды подключения <code>worker</code>-узлов наличием дополнительных параметров<br />
<code>--control-plane</code>, <code>--certificate-key</code>.<br />
<br />
Запустите ее:<br />
<pre><br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...<br />
--control-plane --certificate-key ... <br />
</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>join</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
<br />
По окончании скрипт выводит текст:<br />
<pre><br />
This node has joined the cluster and a new control plane instance was created:<br />
<br />
* Certificate signing request was sent to apiserver and approval was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
* Control plane label and taint were applied to the new node.<br />
* The Kubernetes control plane instances scaled up.<br />
* A new etcd member was added to the local/stacked etcd cluster.<br />
</pre><br />
<br />
=== Проверка состояния процессов ===<br />
<br />
Проверьте состояние дерева процессов:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─kubelet.sh───nsenter_u7s───nsenter───_kubelet.sh───kubelet───11*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-controller───4*[{kube-controller}]<br />
│ │ ├─conmon───kube-scheduler───8*[{kube-scheduler}]<br />
│ │ ├─conmon───etcd───9*[{etcd}]<br />
│ │ ├─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─conmon───kube-apiserver───8*[{kube-apiserver}]<br />
│ │ ├─rootlesskit.sh───crio───8*[{crio}]<br />
│ │ └─7*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
</pre><br />
<br />
Дерево <code>rootless-процессов</code> должно отличаться от дерева процессов основного <code>master-узла</code><br />
отсутствием контейнеров <code>coredns</code>.<br />
<br />
=== Проверка готовности master и worker узлов kubernets ===<br />
<br />
На одном из master-узлов наберите команду:<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host1> Ready control-plane 7h54m v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
&lt;host2> Ready &lt;none&gt; 8m30s v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
...<br />
&lt;hostN> Ready control-plane 55m v1.26.3 10.96.122.&lt;N> <none> ALT cri-o://1.26.2<br />
...<br />
</pre><br />
<br />
=== Использование REST-интерефейсов подключенных master-узлов ===<br />
<br />
По умолчанию на подключенных <code>master-узлах</code> в файле <code>/etc/kubernetes/admin.conf</code> <br />
указан адрес <code>API-интерфейса</code> основного <code>master-узла</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://<master1>:6443<br />
...<br />
</pre> <br />
<br />
Для балансировки нагрузки в файлах конфигурации <code>~user/.kube/config</code> <br />
есть смысл указать адреса <code>API-интерфейсов</code> дополнительных <code>master-узлов</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://<masterN>:6443<br />
...<br />
</pre><br />
<br />
== Создание гетерогенных кластеров, миграция с rootfull кластеров на rootless кластера ==<br />
<br />
В рамках одного кластера могут функционировать как <code>rootfull</code> узла`, так и <code>rootless узлы</code>.<br />
Например имеет смысл в расках <code>rootfull</code> кластера для повышения защищенности кластера <br />
подключать в качестве <code>Worker</code>оа <code>rootless</code> узлы.<br />
<br />
Перед подключением `rootless` узлов необходимо выполнить определенные действия.<br />
<br />
=== Запуск kube-proxy на rootless-узле в rootfull кластере ===<br />
<br />
Для запуска <code>kube-proxy</code> на <code>rootless-узле</code> в <code>rootfull кластере</code> на <code>ControlPlane</code> узле:<br />
<br />
* Наберите команду редактирования <code>ConfigMap</code>а <code>kube-proxy</code>:<br />
<pre><br />
kubectl -n kube-system edit Configmaps kube-proxy<br />
</pre><br />
<br />
* Измение в элементе <code>data.config.conf</code> значение переменной <code>conntrack.maxPerCore</code> с <code>null</code> на <code>0</code>.<br />
<br />
* Выйдите из редактора<br />
<br />
=== Запуск ControlPlane узла на rootless-узле в rootfull кластере ===<br />
<br />
Для запуска <code>ControlPlane</code> на <code>rootless-узле</code> в <code>rootfull кластере</code> на <code>ControlPlane</code> узле:<br />
<br />
* Наберите команду редактирования <code>ConfigMap</code>а <code>kubeadm-config</code>:<br />
<pre><br />
kubectl -n kube-system edit Configmaps kubeadm-config<br />
</pre><br />
<br />
* Измение в элементе <code>data.ClusterConfiguration</code> значение переменной <code>etcd.local.dataDir</code> с <code>/var/lib/etcd</code> на <code>/var/lib/podsec/u7s/etcd</code>.<br />
<br />
* Выйдите из редактора<br />
<br />
== Получениe строки подключения узла к кластеру ==<br />
<br />
=== Получении строки подключения Worker узла к кластеру ===<br />
<br />
В случае, если команда строки подключения утеряна или срок сгенерированного сертификата<br />
истек можно сгенерировать новую строку подключения, выполнив команду:<br />
<pre><br />
joinCommand=$(/usr/bin/kubeadm token create --print-join-command)<br />
</pre><br />
и выполнить команду подключения:<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
$joinCommand <br />
</pre><br />
<br />
=== Получении строки подключения Control-plane узла к кластеру ===<br />
<br />
В определенных случаях `kubeadm init` генерирует только строку подключения `worker` узлов.<br />
Или срок действия сертификата для подключения истек.<br />
<br />
В этом случае есть смысл перегенерировать сертификат:<br />
<pre><br />
cert=$(/usr/bin/kubeadm init phase upload-certs --upload-certs 2>/dev/null | tail -1)<br />
</pre><br />
строку подключения `control-plane` и `worker` узлов к кластеру:<br />
<pre><br />
joinCommand=$(/usr/bin/kubeadm token create --print-join-command)<br />
</pre><br />
и выполнить команду подключения:<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
$joinCommand --control-plane --certificate-key $cert<br />
</pre><br />
<br />
См. [https://stackoverflow.com/questions/51126164/how-do-i-find-the-join-command-for-kubeadm-on-the-master How do I find the join command for kubeadm on the master?]<br />
<br />
== Системный пользователь u7s-admin ==<br />
<br />
Все контейнеры в <code>rootless kubernetes</code>. включая системные работают от имени системного пользователя <code>u7s-admin</code>.<br />
Вы можете для мониторинга работы системы или запуска дополнительного функционала работать в системе от имени этого пользователя.<br />
<br />
Для входа в терминальный режим этого пользователя достаточно в пользователе с правами <code>root</code> набрать команду:<br />
<pre><br />
# machinectl shell u7s-admin@ /bin/bash<br />
</pre><br />
или задав пароль пользователя:<br />
<pre><br />
# passwd u7s-admin<br />
</pre> <br />
зайти в него через <code>ssh</code>.<br />
<br />
Для входа в namespace пользователя наберите команду :<br />
<pre><br />
$ nsenter_u7s<br />
#<br />
</pre><br />
<br />
В рамках своего <code>namespace</code> пользователь <code>u7s-admin</code> имеет права <code>root</code>, оставаясь в рамках системы <br />
с правам пользователя <code>u7s-admin</code>.<br />
<br />
Наличие прав <code>root</code> позволает использовать системные команды,требующих <code>root-привелегий</code> для работы с сетевым, файловым окружением (эти окружения отличаются от системных): <code>ip</code>, <code>iptables</code>, <code>crictl</code>, ...<br />
<br />
С помощью команды <code>crict</code>l можно <br />
* посмотреть наличие образов в системном кэше, <br />
* удалить, загрузить образы<br />
* посмотреть состояние контейнеров, pod'ов<br />
* и т.п. <br />
<br />
Кроме этого <code>namespace</code> пользователя <code>u7s-admin</code> присутствуют файлы и каталоге созданные в рамках данного <br />
<code>namespace</code> и отсутствующие в основной системе.<br />
Например Вы можете посмотреть логи контейнеров в каталоге <code>/var/log/pods</code> и т.п.<br />
<br />
== Особенности разворачивания приложений в rootless kubernetes ==<br />
<br />
При использовании сервисов типа <code>NodePort</code> поднятые в рамках кластера порты в диапазоне <code>30000-32767</code> остаются в <code>namespace</code> пользователя <code>u7s-admin</code>. Для их проброса наружу необходимо в пользователе <code>u7s-admin</code> запустить команду:<br />
<pre><br />
$ nsenter_u7s rootlessctl add-ports 0.0.0.0:&lt;port>:&lt;port>/tcp<br />
</pre><br />
Сервисы типа <code>NodePort</code> из за их небольшого диапазона и "нестабильности" портов при переносе решения в другой кластер довольно редко используются. Рекомендуется вместо них использовать сервисы типа <code>ClusterIP</code> c доступом к ним через <code>Ingress</code>-контроллеры.<br />
<br />
= Разворачивание rootless kubernetes кластера с балансировщиком REST-запросов haproxy =<br />
<br />
Вышеописанный процесс разворачивания обеспечивать только ручную балансировку запросов:<br />
[[Файл:Variant1.drawio.png|840px||center|rootless kubernetes-кластер без балансировщика haproxy]]<br />
<br />
Ручная балансировка запросов к <code>API-интерфейсам</code> <code>master-узлов</code> путем указания у клиентов адресов различных <br />
<code>master-узлов</code> довольно неудобна, так как не обеспечивает равномерного распределения запросов по узлам кластера и не обеспечивает автоматической отказоустойчивости при выходе из строя <code>master-узлов</code>.<br />
<br />
Решает данную проблему установка балансировщика нагрузки <code>haproxy</code>. <br />
[[Файл:Variant_haproxy_1.drawio.png|840px||center|rootless kubernetes-кластер без балансировщика haproxy]]<br />
<br />
Перевод кластера в режим балансировки запросов через haproxy возможен.<br />
Подробности описаны в статье [https://stackoverflow.com/questions/65505137/how-to-convert-a-kubernetes-non-ha-control-plane-into-an-ha-control-plane How to convert a Kubernetes non-HA control plane into an HA control plane?], но данная процедура не гарантирует корректный перевод на всех версиях <code>kubernetes</code> и ее не рекомендуют применять на <code>production</code> кластерах.<br />
<br />
Так что наиболее надежным способом создания кластера с балансировкой запросов является создание нового кластера. <br />
<br />
== Настройка балансировщика REST-запросов haproxy ==<br />
<br />
Балансировщик <code>REST-запросов haproxy</code> можно устанавливать как на отдельный сервер, так на один из серверов кластера.<br />
[[Файл:Variant haproxy master.drawio.png|840px|center|безрамки]]<br />
<br />
<blockquote><br />
'''Если балансировщик устанавливается на <code>rootless</code> сервер кластера, то для балансировщика необходимо выделить отдельный IP-адрес. Если на этом же сервере функционируют локальный регистратор (<code>registry.local</code>) и сервер подписей (<code>sigstore.local</code>), то IP-адрес балансировщика может совпадать c IP-адресами этих сервисов. <br />
''' <br />
</blockquote><br />
<br />
<blockquote><br />
'''Если планируется создание отказоустойчивого решения на основе нескольких серверов <code>haproxy</code>, то для них кроме собственного <code>IP-адреса</code> необходимо будет для всех серверов <code>haproxy</code> выделить один общий <code>IP-адрес</code>, который будет иметь <code>master-балансировщик</code>.<br />
''' <br />
</blockquote><br />
<br />
<br />
Полная настройка отказоустойчивого кластера <code>haproxy</code> из 3-х узлов описана в документе [https://www.altlinux.org/ALT_Container_OS_%D0%BF%D0%BE%D0%B4%D0%B2%D0%B5%D1%82%D0%BA%D0%B0_K8S._%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_HA_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B0 ALT Container OS подветка K8S. Создание HA кластера].<br />
<br />
Здесь же мы рассмотрим создание и настройка с одним сервером <code>haproxy</code> с балансировкой запросов на <code>master</code>-узлы.<br />
<br />
Установите пакет <code>haproxy</code>:<br />
<br />
<pre># apt-get install haproxy</pre><br />
Отредактируйте конфигурационный файл <code>/etc/haproxy/haproxy.cfg</code>:<br />
<br />
<ul><br />
<li>добавьте в него описание <code>frontend</code>’a <code>main</code>, принимающего запросы по порту <code>8443</code>:<br />
<pre><br />
frontend main<br />
bind *:8443<br />
mode tcp<br />
option tcplog<br />
default_backend apiserver<br />
</pre></li><br />
<li>добавьте описание <code>backend</code>’а <code>apiserver</code>:<br />
<pre><br />
backend apiserver<br />
option httpchk GET /healthz<br />
http-check expect status 200<br />
mode tcp<br />
option ssl-hello-chk<br />
balance roundrobin<br />
server master01 &lt;IP_или_DNS_начального_мастер_узла>:6443 check<br />
</pre></li><br />
<li>запустите <code>haproxy</code>:</li></ul><br />
<br />
<pre># systemctl enable haproxy<br />
# systemctl start haproxy</pre><br />
<br />
== Инициализация master-узла ==<br />
<br />
==== Инициализация мастер-узла при работа с балансировщиков haproxy ====<br />
<br />
При установке начального master-узла необходимо параметром <code>control-plane-endpoint</code> указать URL балансировщика <code>haproxy</code>:<br />
<br />
<pre># kubeadm init --apiserver-advertise-address 192.168.122.80 --control-plane-endpoint &lt;IP_адрес_haproxy&gt;:8443</pre><br />
При запуске в параметре <code>--apiserver-advertise-address</code> укажите IP-адрес API-интерфейса <code>kube-apiserver</code>.<br />
<br />
'''IP-адреса в параметрах''' <code>--apiserver-advertise-address</code> '''и''' <code>--control-plane-endpoint</code> '''должны отличаться. Если Вы развернули''' <code>haproxy</code> '''на том же мастер-узле, поднимите на сетевом нтерфейсе дополнительный IP-адрес и укажите его в параметре''' <code>--control-plane-endpoint</code>'''.<br />
<br />
В результате инициализации <code>kubeadm</code> выведет команды подключения дополнительных <code>control-plane</code> и <code>worker</code> узлов:<br />
<pre><br />
...<br />
You can now join any number of the control-plane node running the following command on each as root:<br />
<br />
kubeadm join &lt;IP_адрес_haproxy>:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:... \<br />
--control-plane --certificate-key ...<br />
<br />
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!<br />
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use<br />
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.<br />
<br />
Then you can join any number of worker nodes by running the following on each as root:<br />
<br />
kubeadm join <IP_адрес_haproxy>:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:...<br />
...<br />
</pre><br />
Обратите внимание - в командах присоединения узлов указывается не URL созданного начального master-узла (<code>&lt;IP_или_DNS_начального_мастер_узла&gt;:6443</code>), а URL <code>haproxy</code>.<br />
<br />
В сформированных файлах конфигурации <code>/etc/kubernetes/admin.conf</code>, <code>~/.kube/config</code> также указывается URL <code>haproxy</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://&lt;IP_адрес_haproxy>:8443<br />
</pre><br />
То есть вся работа с кластеров в дальнейшем идет через балансировщик запросов <code>haproxy</code>.<br />
<br />
Для перевода узла в состояние <code>Ready</code>, запуска coredns Pod’ов запустите flannel<br />
<br />
==== Запуск сетевого маршрутизатора для контейнеров kube-flannel ====<br />
<br />
На <code>master-узле</code> под пользоваталем <code>root</code> выполните команду:<br />
<br />
<pre># kubectl apply -f /etc/kubernetes/manifests/kube-flannel.yml<br />
Connected to the local host. Press ^] three times within 1s to exit session.<br />
[INFO] Entering RootlessKit namespaces: OK<br />
namespace/kube-flannel created<br />
clusterrole.rbac.authorization.k8s.io/flannel created<br />
clusterrolebinding.rbac.authorization.k8s.io/flannel created<br />
serviceaccount/flannel created<br />
configmap/kube-flannel-cfg created<br />
daemonset.apps/kube-flannel-ds created<br />
Connection to the local host terminated.</pre><br />
После завершения скрипта в течении минуты настраиваются сервисы мастер-узла кластера. По ее истечении проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
== Подключение дополнительных master-узлов ==<br />
<br />
<br />
=== Установка тропы PATH поиска исполняемых команд ===<br />
<br />
Измените переменную <code>PATH</code>:<br />
<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
</pre><br />
<br />
=== Подключение master (control plane) узла ===<br />
<br />
Скопируйте строку подключения <code>control-plane</code> узла и вызовите ее:<br />
<br />
<pre># kubeadm join &lt;IP_адрес_haproxy&gt;:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:... \<br />
--control-plane --certificate-key ...</pre><br />
В результате работы команда kubeadm выведет строки:<br />
<pre><br />
This node has joined the cluster and a new control plane instance was created:<br />
<br />
* Certificate signing request was sent to apiserver and approval was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
* Control plane label and taint were applied to the new node.<br />
* The Kubernetes control plane instances scaled up.<br />
* A new etcd member was added to the local/stacked etcd cluster.<br />
...<br />
Run 'kubectl get nodes' to see this node join the cluster.<br />
</pre><br />
Наберите на вновь созданном (или начальном)<code>control-plane</code> узле команду:<br />
<br />
<pre># kubectl get nodes<br />
NAME STATUS ROLES AGE VERSION<br />
&lt;host1&gt; Ready control-plane 4m31s v1.26.3<br />
&lt;host2&gt; Ready control-plane 26s v1.26.3<br />
</pre><br />
Обратите внимание, что роль (ROLES) обоих узлов - <code>control-plane</code>.<br />
<br />
Наберите команду:<br />
<pre><br />
# kubectl get all -A<br />
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
kube-flannel pod/kube-flannel-ds-2mhqg 1/1 Running 0 153m 10.96.0.1 <host1> <none> <none><br />
kube-flannel pod/kube-flannel-ds-95ht2 1/1 Running 0 153m 10.96.122.68 <host2> <none> <none><br />
...<br />
kube-system pod/etcd-<host1> 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/etcd-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-apiserver-<host1> 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-apiserver-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-controller-manager-<host1> 1/1 Running 1 (170m ago) 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-controller-manager-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-proxy-9nbxz 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-proxy-bnmd7 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-scheduler-<host1> 1/1 Running 1 (170m ago) 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-scheduler-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
...<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR<br />
kube-flannel daemonset.apps/kube-flannel-ds 2 2 2 3 3 <none> 153m kube-flannel registry.local/k8s-c10f1/flannel:v0.19.2 app=flannel<br />
kube-system daemonset.apps/kube-proxy 2 2 2 2 2 kubernetes.io/os=linux 174m kube-proxy registry.local/k8s-c10f1/kube-proxy:v1.26.3 k8s-app=kube-proxy<br />
...<br />
</pre><br />
Убедитесь, что сервисы <code>pod/etcd</code>, <code>kube-apiserver</code>, <code>kube-controller-manager</code>, <code>kube-scheduler</code>, <code>kube-proxy</code>, <code>kube-flannel</code> запустились на обоих control-plane узлах.<br />
<br />
=== Добавление master-узла в балансироващик haproxy ===<br />
<br />
Для балансировки запросов по двум серверам добавьте URL подключенного <code>control-plane</code> узла в файл конфигурации <code>/etc/haproxy/haproxy.cfg</code>:<br />
<pre><br />
backend apiserver<br />
option httpchk GET /healthz<br />
http-check expect status 200<br />
mode tcp<br />
option ssl-hello-chk<br />
balance roundrobin<br />
server master01 &lt;IP_или_DNS_начального_мастер_узла>:6443 check<br />
server master02 &lt;IP_или_DNS_подключенного_мастер_узла>:6443 check<br />
</pre><br />
и перезапустите <code>haproxy</code>:<br />
<br />
<pre># systemctl restart haproxy</pre><br />
Логи обращений и балансировку запросов между узлами можно посмотреть командой:<br />
<br />
<pre># tail -f /var/log/haproxy.log</pre><br />
<br />
== Подключение worker-узлов ==<br />
<br />
Подключение дополнительных worker-узлов происходит аналогично описанному выше в главе '''Инициализация и подключение worker-узла'''.<br />
<br />
== Настройка отказоустойчивого кластера серверов haproxy, keepalived ==<br />
<br />
=== Масштабирование haproxy, установка пакетов ===<br />
<br />
Если необходимо создать отказоустойчивое решение допускающее выход <code>haproxy</code>-севрера из строя <br />
установите <code>haproxy</code> на несколько серверов. Файлы конфигурации <code>haproxy<.code> на всех сервервх должны быть идентичны.<br />
<br />
Для контроля доступности <code>haproxy</code> и переназначений виртуального адреса дополнительно установите на каждом сервис <code>keepalived</code>:<br />
<pre><br />
# apt-get install haproxy keepalived<br />
</pre><br />
<br />
=== Конфигурирование keepalived ===<br />
<br />
[[Файл:Variant haproxy keepalived.png|840px|безрамки|центр|kubeenetes кластер с haproxy и keepalived]]<br />
<br />
Создайте файл конфигурации 'keepalived' ''/etc/keepalived/keepalived.conf'':<br />
<br />
! /etc/keepalived/keepalived.conf<br />
! Configuration File for keepalived<br />
global_defs {<br />
router_id LVS_K8S<br />
}<br />
vrrp_script check_apiserver {<br />
script "/etc/keepalived/check_apiserver.sh"<br />
interval 3<br />
weight -2<br />
fall 10<br />
rise 2<br />
}<br />
<br />
vrrp_instance VI_1 {<br />
state MASTER<br />
interface br0<br />
virtual_router_id 51<br />
priority 101<br />
authentication {<br />
auth_type PASS<br />
auth_pass 42<br />
}<br />
virtual_ipaddress {<br />
10.150.0.160 <br />
}<br />
track_script {<br />
check_apiserver<br />
}<br />
}<br />
<br />
На одном из узлов установите параметр ''state'' в значение ''MASTER'' и параметр ''priority'' в значение ''101''.<br />
На остальных параметр ''state'' в значение ''BACKUP'' и параметр ''priority'' в значение ''100''.<br />
<br />
Скрипт ''/etc/keepalived/check_apiserver.sh'' проверяет доступность балансировщика ''haproxy'':<br />
#!/bin/sh<br />
<br />
errorExit() {<br />
echo "*** $*" 1>&2<br />
exit 1<br />
}<br />
<br />
APISERVER_DEST_PORT=8443<br />
APISERVER_VIP=10.150.0.160<br />
curl --silent --max-time 2 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/"<br />
if ip addr | grep -q ${APISERVER_VIP}; then<br />
curl --silent --max-time 2 --insecure https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/"<br />
fi<br />
<br />
Параметр ''APISERVER_DEST_PORT'' задает порт балансировщиков ''haproxy'', параметр ''APISERVER_VIP'' виртуальный адрес,<br />
через который будут взаимодействовать ''master'' (''control plane'') узлы кластера ''k8s''.<br />
<br />
Скрипт проверяет работоспособность ''haproxy'' на локальной машине.<br />
<br />
Подробности см. на [[https://www.altlinux.org/Keepalived]]<br />
А если в настоящее время виртуальный адрес принадлежит текущему узлу, то и работоспособность ''haproxy'' через виртуальный адрес. <br />
<br />
Добавьте флаг на выполнение скрипта:<br />
chmod a+x /etc/keepalived/check_apiserver.sh<br />
<br />
При работающем балансировщике и хотя бы одному доступному порту ''6443'' на ''master-узлах'' скрипт<br />
должен завершаться с кодом ''0''.<br />
<br />
Подробности см. на [[Keepalived]]<br />
<br />
= Установка и настройка ingress-контролера =<br />
<br />
<code>Ingress-контроллер</code> обеспечивает переадресацию <code>http(s)</code> запросов по указанным шаблонам на внутренние сервисы <code>kubernetes-кластера</code>. <br />
Для <code>bare-metal</code> решений и решений на основе виртуальных машин наиболее приемлимым является<br />
[https://github.com/kubernetes/ingress-nginx ingress-nginx контроллер].<br />
<br />
При применении <code>Ingress-контроллера</code> нет необходимости создавать <code>Nodeport-порты</code> и пробрасывать их из <code>namespace</code> пользователя <code>u7s-admin</code>. <code>Ingress-контроллер</code> переадресует <code>http{s)</code> запрос через сервис непосредственно на порты <code>Pod</code>'ов входящих в реплики <code>deployment</code>.<br />
<br />
== Установка и настройка ingress-nginx-контролера в кластере ==<br />
<br />
[[Файл:Ingress.png|840px|безрамки|центр|Использование ingress-контроллера]]<br />
Для установки <code>Ingress-контроллера</code> скопируйте его YAML-манифест:<br />
<pre><br />
curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.0/deploy/static/provider/baremetal/deploy.yaml -o ingress-nginx-deploy.yaml<br />
</pre><br />
<br />
Выберите свободный порт в диапазона <code>30000 - 32767</code> (например <code>31000</code>) и добавьте его в элемент <br />
<code>spec.ports.appProtocol==http</code><br />
Yaml-описании <code>kind==Service</code>:<br />
<pre><br />
...<br />
---<br />
kind: Service<br />
spec:<br />
ports:<br />
- appProtocol: http<br />
...<br />
nodePort: 31000<br />
...<br />
</pre><br />
Если в Вашем решении используется ТОЛЬКО локальный регистратор <code>registry.local</code> <br />
* создайте алиасы образам nginx:<br />
<pre><br />
podman tag registry.k8s.io/ingress-nginx/controller:v1.8.0@sha256:744ae2afd433a395eeb13dc03d3313facba92e96ad71d9feaafc85925493fee3 registry.local/ingress-nginx/controller:v1.8.0<br />
podman tag registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407@sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b registry.local/ingress-nginx/kube-webhook-certgen:v20230407<br />
</pre><br />
и поместите их в локальный регистратор:<br />
<pre><br />
podman push --tls-verify=false --sign-by='<EMAIL>' registry.local/ingress-nginx/controller<br />
podman push --tls-verify=false --sign-by='<EMAIL>' registry.local/ingress-nginx/kube-webhook-certgen<br />
</pre><br />
* исправьте имена образов в скачанном нанифесте на имена образов в локальном регистраторе.<br />
<br />
Запустите Ingress-nginx-контролер:<br />
<pre><br />
kubectl apply -f ingress-nginx-deploy.yaml<br />
</pre><br />
<br />
На одном или нескольких kubernet-узлах (эти узла в дальнейшем нужно прописать в файле конфигурации балансировщика <code>haproxy</code>) пробросьте порт <code>nginx-контроллера</code> (<code>31000</code>) из <code>namespace</code> пользователя <code>u7s-admin</code> в сеть <code>kubernetes</code>:<br />
<pre><br />
nsenter_u7s rootlessctl add-ports 0.0.0.0:31000:31000/tcp<br />
</pre><br />
<br />
=== Настройка Ingress-правил ===<br />
<br />
Kubernetes поддерживает манифесты типа Ingress (kind: Ingress) описывающие правила переадресации запросов URL http-запррса на внутренние порты сервисов (kind: Service) kubernetes. Сервисы в свою очередь перенаправляют запросы на реплики Pod'ов, входящих в данный сервис.<br />
<br />
Общий вид Ingress-манифеста:<br />
<pre><br />
apiVersion: networking.k8s.io/v1<br />
kind: Ingress<br />
metadata:<br />
name: <ingress_имя><br />
spec:<br />
ingressClassName: nginx <br />
rules:<br />
- host: <домен_1><br />
http:<br />
paths:<br />
- path: /<br />
pathType: Prefix<br />
backend:<br />
service:<br />
name: <имя_сервиса_1><br />
port:<br />
number: 80<br />
- path: /<тропа_1><br />
pathType: Prefix<br />
backend:<br />
service:<br />
name: <имя_сервиса_2><br />
port:<br />
number: 80<br />
- host: <домен_2><br />
...<br />
</pre><br />
Где:<br />
* <code>host: <домен_1></code>, <code><домен_2></code>, ... - домены WEB-серверов на которых приходит запрос; <br />
* <code>path:/></code>, <code>path:/<тропа_1></code> - тропы (префиксы запросов после домена) <br />
* <code>pathType: Prefix</code> - тип троп: <code>Prefix</code> или <code>Exact</code>;<br />
* <code>service:</code> - имя сервиса на который перенаправляется запрос, если полученный запрос соответсвует правилу;<br />
* <code>port</code> - номер порта на который перенаправляется запрос.<br />
<br />
Если запросу соответствует несколько правил, выбирается правило с наиболее длинным префиксом.<br />
<br />
Подробности смотри в [https://kubernetes.io/docs/concepts/services-networking/ingress/ Kubernetes: Ingress]<br />
<br />
== Настройка haproxy и DNS ==<br />
<br />
Добавьте в файлы конфигурации <code>haproxy</code> <code>/etc/haproxy/haproxy.conf</code> переадресацию запросов на порт <code>80</code> (<code>http</code>) по IP-адресу балансировщика haproxy на IP-адреса <code>kubernet-узлов</code> на которых выбранный порт <code>nginx-контроллера</code> (<code>31000</code>) проброшен из <code>namespace</code> пользователя <code>u7s-admin</code> в сеть <code>kubernetes</code>:<br />
<pre><br />
frontend http<br />
bind *:80<br />
mode tcp<br />
option tcplog<br />
default_backend http<br />
<br />
backend http<br />
mode tcp<br />
balance roundrobin<br />
server <server1> <ip1>:31000 check<br />
server <server2> <ip2>:31000 check<br />
</pre><br />
<br />
Заведите DNS-запись связывающую DNS-имя http-сервиса с IP-адресам <code>haproxy</code>-сервера.<br />
<br />
= Выбор версии kubernetes, имени регистратора и платформы =<br />
<br />
Во время разворачивания узла командами<br />
<pre><br />
kubeadm init <br />
kubeadm join ...<br />
</pre><br />
или при создании архива образов командой <br />
<pre><br />
podsec-k8s-save-oci ...<br />
</pre><br />
есть возможность установкой переменных среды указать версию kubernetes, имя регистратора и платформы:<br />
* U7S_KUBEVERSION - версия kubernetes (v1.26.9, v1.27.7, ...);<br />
* U7S_REGISTRY - имя регистратора (registry.k8s.io, registry.altlinux.org, registry.local);<br />
* U7S_PLATFORM - имя платформы (k8s-c10f1 , k8s-p10, ...)<br />
<br />
== Выбор версии kubernetes ==<br />
<br />
Начиная с версии <code>1.0.9</code> поддерживается возможность выбора устанавливаемой версии <code>kubernetes</code>.<br />
<br />
=== Указании версии основных kubernetes образов ===<br />
<br />
Основные kubernetes-образы загружаются в момент инициализации узла командой <code>kubeadm</code>.<br />
В список основных образов входят:<br />
<pre><br />
kube-apiserver:<версия_kubernetes><br />
kube-controller-manager:<версия_kubernetes><br />
kube-scheduler:<версия_kubernetes><br />
kube-proxy:<версия_kubernetes><br />
pause:<версия__образа_pause><br />
etcd:<версия__образа_etcd><br />
coredns:<версия__образа_coredns><br />
</pre><br />
<br />
Тег образов <code>kube-*</code> совпадает с полным номером версии kubernetes типа v1.<minor>.<patch>. Например <code>v1.26.9</code>.<br />
<br />
Теги образов <code>pause</code>, <code>etcd</code>, <code>coredns</code> "зашиты" как статические переменные в <code>kubeadm</code> и могут отличаться в разных версиях <code>kubernetes</code>.<br />
<br />
Получить список образов для текущей версии <code<kuvernetes</code> можно командой:<br />
<pre><br />
# /usr/bin/kubeadm config images list 2>/dev/null<br />
</pre><br />
Пример вывода:<br />
<pre><br />
registry.k8s.io/kube-apiserver:v1.26.10<br />
registry.k8s.io/kube-controller-manager:v1.26.10<br />
registry.k8s.io/kube-scheduler:v1.26.10<br />
registry.k8s.io/kube-proxy:v1.26.10<br />
registry.k8s.io/pause:3.9<br />
registry.k8s.io/etcd:3.5.9-0<br />
registry.k8s.io/coredns/coredns:v1.9.3<br />
</pre><br />
<br />
Выбор версии определяет переменная среды <code>U7S_KUBEVERSION</code>.<br />
<br />
На <code>1.11.2023</code> при значении <code>U7S_REGISTRY</code> <code>registry.altlinux.org</code> переменная <code>U7S_KUBEVERSION</code> может принимать следующие значения:<br />
<br />
* Mинор версия v1.26:<br />
* <code>v1.26.6</code>; <br />
* <code>v1.26.9</code>;<br />
* <code>v1.26.11</code>; <br />
<br />
* Mинор версия v1.27:<br />
* <code>v1.27.11</code>;<br />
<br />
* Mинор версия v1.28:<br />
* <code>v1.28.7</code>;<br />
<br />
Данный список относится к версиям регистратора <code>registry.altlinux.org</code>. При использовании нативного регистратора <code>registry.k8s.io</code> (пустое значение <code>export U7S_REGISTRY=</code>) можно указывать любую доступную на <code>registry.k8s.io</code> версию.<br />
<br />
Примеры:<br />
<pre><br />
export U7S_REGISTRY=registry.altlinux.org<br />
export U7S_KUBEVERSION=v1.26.11<br />
</pre><br />
<br />
<pre><br />
export U7S_REGISTRY=registry.local<br />
export U7S_KUBEVERSION=v1.27.11<br />
</pre><br />
<br />
<pre><br />
export U7S_REGISTRY=<br />
export U7S_KUBEVERSION=v1.27.5<br />
</pre><br />
<br />
<br />
По умолчанию (при отсутствии значения переменной <code>U7S_KUBEVERSION</code>) принимается максимальная версия образа <br />
<code>kube-apiserver</code>.<br />
<br />
Если номер указанной минорной версии <code>kubernetes</code> отличается от текущего, при вызове команды <code>kubeadm</code><br />
производится удаление текущих <code>rpm-пакетов</code> <code>kubernetes*</code>, <code>cri-o</code> и установка <code>rpm-пакетов</code> указанной версии.<br />
<br />
Возможна ситуация, когда на регистраторе образов отсутствует версия образа, полученная в результате выполнения команды: <br />
<pre><br />
/usr/bin/kubeadm config images list<br />
</pre><br />
Если в переменные среды добавить переменную:<br />
<pre><br />
export U7S_SETAVAILABLEIMAGES=yes<br />
</pre><br />
то в качестве стандартного образа принимается образ с максимальной версией в рамках данной минорной версии <br />
(<code>1.26</code>, <code>1.27</code>, <code>1.28</code>).<br />
Данному образу присваивается тег, полученнфй в результате выполнения команды <code> kubeadm config images</code>.<br />
<br />
=== Указании версии дополнительных kubernetes образов ===<br />
<br />
Кроме основных образов при разворачивании кластера используются дополнительные образы:<br />
* flannel:<U7S_FLANNEL_TAG>;<br />
* flannel-cni-plugin:<U7S_FLANNELCNIPLUGIN_TAG><br />
* cert-manager-webhook:<U7S_CERTMANAGER_TAG>;<br />
* cert-manager-controller:<U7S_CERTMANAGER_TAG>;<br />
* cert-manager-cainjector:<U7S_CERTMANAGER_TAG>.<br />
<br />
> При развертывании <code>kubernetes</code> используется только образ <code>flannel</code>, остальные образы используются только командой <code>podsec-k8s-save-oci</code> при формировании архива пакетов для локального регистратора <code>registry.local</code>.<br />
<br />
Если переменным среды <br />
<code>U7S_FLANNEL_TAG</code>, <br />
<code>U7S_FLANNELCNIPLUGIN_TAG</code>, <br />
<code>U7S_CERTMANAGER_TAG</code> не присвоены значения,<br />
то для каждого образа определяется максимальная версия в регистраторе <br />
и загружается найденная версия образа.<br />
<br />
При необходимости можно изменить версию образа экпортировав перед запускам команды соответствующую переменную. Например:<br />
<pre><br />
export U7S_FLANNEL_TAG=v0.19.2<br />
</pre><br />
<br />
== Выбор исходного регистратора kubernetes-образов ==<br />
<br />
Во время инициализации <code>master-узла</code> кластера (<code>kubeadm init</code>) или во время подключения узла к кластеру (<code>kubeadm join</code>) команда <code>kubeadm</code> может загружать образы с различных регистраторов образов и с различными префиксами.<br />
<br />
Выбор регистратора и префикса образов определяет переменная среды <code>U7S_REGISTRY</code>. <br />
Если переменная не задана регистратор префикс определяется автоматически на основе конфигурационных файлов <code>/etc/os-release</code> и <code>/etc/hosts</code>. <br />
<br />
Переменная среды <code>U7S_REGISTRY</code> может принимать следующие основные значения:<br />
* пустое значение;<br />
* <code>registry.altlinux.org</code>;<br />
* <code>registry.local</code>;<br />
* ...<br />
<br />
=== Нативные kubernetes-образы ===<br />
<br />
<pre><br />
export U7S_REGISTRY=<br />
</pre><br />
Если переменная <code>U7S_REGISTRY</code> установлена в пустое значение образы загружаются со стандартного регистратора образов <code>kubernetes</code>.<br />
<br />
=== Образы altlinux ===<br />
<br />
==== Регистратор registry.altlinux.org ====<br />
<br />
<pre><br />
export U7S_REGISTRY=registry.altlinux.org<br />
</pre><br />
С регистратора <code>altlinux</code> устанавливаются образы при наличии доступа в Интернет.<br />
<br />
==== Локальный регистратор ====<br />
<br />
<pre><br />
export U7S_REGISTRY=registry.local<br />
</pre><br />
<br />
Локальный регистратор используется в сертифицированных дистрибутивах, которые содержат kubernetes-образы на установочном диске.<br />
<br />
Локальный регистратор образов <code>registry.local</code> может обеспечивать:<br />
* разворачивание кластера без доступа в Интернет;<br />
* ускоренное разворачивание как кластера, так и проектов, разворачиваемых в его рамках, так как образы необходимые для запуска <code>Pod</code>'ов загружаются по локальной сети; <br />
* высокий уровень защищенности системы путем установки политик разрешающих загрузку только подписанных образов и только с локального регистратора <code>registry.local</code>.<br />
<br />
Пакет <code>podsec</code> обеспечивает:<br />
* Установку на рабочих местах клиентов и узлах <code>kubernetes</code> политик доступа к образом для различных категория пользователей (скрипт <code>podsec-create-policy</code>).<br />
* Разворачивание на одном узлов локального регистратора образов и сервера подписей образов (скрипт <code>podsec-create-services</code>). <br />
* Загрузку с регистратора <code>registry.altlinux.org</code> образов необходимых для разворачивания <code>kubernetes</code> и формирования максимально сжатого (<200Mb) архива. (скрипты <code>podsec-k8s-save-oci</code>, <code>podsec-save-oci</code>)<br />
* разворачивание образов из архива, их подпись размещение на локальном регистраторе (скрипт <code>podsec-load-sign-oci</code>). <br />
<br />
В зависимости от значения переменных <code>U7S_REGISTRY</code>, <code>U7S_PLATFORM</code>, <code>U7S_KUBEVERSION</code> скрипт <code>podsec-k8s-save-oci</code> формирует архив образов различных версий kubernetes:<br />
* <code>registry.local/k8s-c10f1</code> - архив образов для сертифицированного дистрибутива <code>c10</code> на основе набора образов с регистратора <code>registry.altlinux.org</code> с платформой <code>k8s-c10f1</code>;<br />
* <code>registry.local/k8s-p10</code> - архив образов для несертифицированного дистрибутива <code>p10</code> на основе набора образов с регистратора <code>registry.altlinux.org</code> с платформой <code>k8s-p10</code>;<br />
<br />
Локальный регистратор <code>registry.local</code> может также хранить подписанные образы и запускаемых в рамках кластера проектов. Необходимо только, чтобы каждый образ в рамках локального регистратор <code>registry.local</code> имел префикс. Образы типа <code>registry.local/<имя_образа></code> не допускаются, так как для них трудно определить "подписанта" образа.<br />
<!--<br />
* <code>export U7S_REGISTRY=registry.local/k8s-c10f1</code> - образы для сертифицированного дистрибутива <code>c10</code>;<br />
* <code>export U7S_REGISTRY=registry.local/k8s-p10</code> - образы для несертифицированного дистрибутива <code>p10</code>.<br />
--><br />
<br />
===== podsec-create-policy - настройка политики доступа к образам различным категориям пользователей =====<br />
<br />
'''Формат''':<br />
<pre>podsec-create-policy <ip-адрес_регистратора_и_сервера_подписей></pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт <code>podsec-create-policy</code> формирует в файлах <code>/etc/containers/policy.json</code>, <br />
<code>/etc/containers/registries.d/default.yaml</code> максимально защищенную политику доступа к образам - по умолчанию допускается доступ только к подписанным образам локального регистратора <code>registry.local</code>.<br />
Данная политика распространяется как на пользователей имеющих права суперпользователя, так и на пользователей группы <code>podsec</code>, создаваемые podsec-скриптом <code>podsec-create-podmanusers</code>. <br />
<br />
Пользователи группы <code>podsec-dev</code>, создаваемые podsec-скриптом <code>podsec-create-imagemakeruser</code> имеют неограниченные права на доступ, формирования образов, их подпись и помещение на локальный регистратор <code>registry.local</code>.<br />
<br />
В разворачиваниях kubernetes не требующих таких жестких ограничений в политике доступа и работы с образами политики могут быть смягчены путем модифицирования cистемных файлов политик <code>/etc/containers/policy.json</code>, <code>/etc/containers/registries.d/default.yaml</code> или файлов установки политик пользователей <code>~/.config/containers/policy.json</code>, <code>~/.config/containers/registries.d/default.yaml</code>.<br />
<br />
===== podsec-create-services - разворачивание локального регистратора образов и сервера подписей образов =====<br />
<br />
Скрипт <code>podsec-create-services</code> обеспечивает разворачивание локального регистратора образов и сервера подписей образов.<br />
<br />
===== Поддержка электронной подписи образов =====<br />
<br />
Для <code>kubernetes-образов</code>, хранящихся в архиве образов распаковку образов, их подпись и размещение на локальном регистраторе <code>registry.local</code> обеспечивает скрипт <code>podsec-load-sign-oci</code> запускаемый пользователем группы <code>podsec-dev</code>.<br />
<br />
Для других образов пользователь группы <code>podsec-dev</code> должен создать образ в домене локального регистратора <code>registry.local/</prefix>/</code> и поместить его в регистратор командой:<br />
<pre>podman push --tls-verify=false --sign-by="<email-подписанта" <образ></pre><br />
<br />
Образ в домене <code>registry.local/</prefix>/</code> может быть получен:<br />
* присваивании алиаса стороннему образу:<br />
<pre>podman tag <сторонний_образ> registry.local/</prefix>/<локальный_образ></pre><br />
* сборки образов через <code>Dockerfile</code>.<br />
<pre>podman build -t registry.local/</prefix>/<локальный_образ> ...</pre><br />
<br />
== Указание платформы ==<br />
<br />
Кроме имени регистратора kubernetes-образы altlinux содержит в имени (например registry.altlinux.org/k8s-p10/kube-apiserver) название платформы:<br />
* k8s-p10 - образы для дистрибутива p10;<br />
* k8s-c10f1 - образы сертифицированного дистрибутива c10;<br />
* test_k8s - тестовые образы;<br />
* ...<br />
<br />
Платформу устанавливаемых образов можно указать в переменной <code>U7S_PLATFORM</code>. Например:<br />
<pre><br />
export U7S_PLATFORM=test_k8s<br />
</pre><br />
<br />
== Автоматический выбор регистратора образов и платформы ==<br />
<br />
Если переменная <code>U7S_REGISTRY</code> не установлена, ее значения вычисляется автоматически по следующему алгоритму:<br />
<br />
* Если файл <code>/etc/hosts</code> содержит описание хоста <code>registry.local</code> префикс переменной <code>U7S_REGISTRY</code> принимает значение <code>registry.local/</code>, иначе <code>registry.altlinux.org/</code>.<br />
* Если переменная <code>CPE_NAME</code> файла <code>/etc/os-release</code> содержит значение <code>spserver</code> суффикс переменной <code>U7S_PLATFORM</code> принимает значение <code>k8s-c10f1</code>, иначе <code>k8s-p10</code>.<br />
<br />
= podsec-k8s-rbac - Поддержка управление доступом на основе ролей (RBAC) =<br />
<br />
В пакет <code>podsec-k8s-rbac</code> входит набор скриптов для работы с <code>RBAC</code> - <code>Role Based Access Control</code>:<br />
* <code>podsec-k8s-rbac-create-user</code> - создание <code>RBAC-пользователя</code>;<br />
* <code>podsec-k8s-rbac-create-kubeconfig</code> - создание ключей, сертификатов и файла конфигурации <code>RBAC-пользователя</code>;<br />
* <code>podsec-k8s-rbac-create-remoteplace</code> - создание удаленного рабочего места;<br />
* <code>podsec-k8s-rbac-bindrole</code> - привязывание пользователя к кластерной или обычной роли;<br />
* <code>podsec-k8s-rbac-get-userroles</code> - получить список кластерные и обычных ролей пользователя;<br />
* <code>podsec-k8s-rbac-unbindrole</code> - отвязывание пользователя от кластерной или обычной роли.<br />
<br />
== podsec-k8s-rbac-create-user - создание RBAC-пользователя ==<br />
<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-user имя_пользователя</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт:<br />
* создает RBAC пользователя<br />
* создает в домашнем директории каталог .kube<br />
* устанавливаются соответствующие права доступа к каталогам.<br />
<br />
== podsec-k8s-rbac-create-kubeconfig - создание ключей, сертификатов и файла конфигурации RBAC-пользователя ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-kubeconfig имя_пользователя[@<имя_удаленного_пользователя>] [группа ...]</pre><br />
<br />
'''Описание''':<br />
Скрипт должен вызываться администратором безопасности средства контейнеризации.<br />
<br />
Для <code>rootless</code> решения имя удаленного пользователя принимается <code>u7s-admin</code>.<br />
<br />
Для <code>rootfull</code> решения необходимо после символа <code>@</code> указать <code>имя удаленного пользователя</code>.<br />
<br />
Скрипт в каталоге <code>~имя_пользователя/.kube производит</code>:<br />
* Создании личного (private) ключа пользователя (файл <code>имя_пользователя.key</code>).<br />
* Создание запроса на подпись сертификата (CSR) (файл <code>имя_пользователя.key</code>).<br />
* Запись <code>запроса на подпись сертификата CSR </code>в кластер.<br />
* Подтверждение <code>запроса на подпись сертификата (CSR)</code>.<br />
* Создание <code>сертификата</code> (файл <code>имя_пользователя.crt</code>).<br />
* Проверку корректности сертификата<br />
* Формирование файла конфигурации пользователя (файл <code>config</code>)<br />
* Добавление контекста созданного пользователя<br />
<br />
== podsec-k8s-rbac-create-remoteplace - создание удаленного рабочего места ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-remoteplace ip-адрес</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит настройку удаленного рабочего места пользователя путем копирования его конфигурационного файла.<br />
<br />
== podsec-k8s-rbac-bindrole - привязывание пользователя к кластерной или обычной роли ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-bindrole имя_пользователя role|role=clusterrole|clusterrole роль имя_связки_роли [namespace]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит привязку пользователя к обычной или кластерной роли используя имя_связки_роли.<br />
<br />
'''Параметры''':<br />
<br />
* имя_пользователя должно быть создано командой podsec-k8s-rbac-create-user и сконфигурировано на доступ к кластеру командой podsec-k8s-rbac-create-kubeconfig;<br />
* тип роли может принимать следующие значения:<br />
* role - пользователь привязывется к обычной роли с именем <роль> (параметр namespace в этом случае обязателен);<br />
* role=clusterrole - пользователь привязывется к обычной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае обязателен);<br />
* clusterrole - пользователь привязывется к кластерной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае должен отсутствовать).<br />
* роль - имя обычной или кластерной роли в зависимости от предыдущего параметра;<br />
* имя_связки_роли - имя объекта класса rolebindings или clusterrolebindings в зависимости от параметра тип роли. В рамках этого объекта к кластерной или обычной роли могут быть привязаны несколько пользователей.<br />
* namespace - имя namespace для обычной роли.<br />
<br />
== podsec-k8s-rbac-get-userroles - получить список кластерные и обычных ролей пользователя ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-get-userroles имя_пользователя [showRules]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт формирует список кластерные и обычных ролей которые связаны с пользователем. При указании флага <code>showRules</code>, для каждой роли указывается список правил ("<code>rules:[...]</code>"), которые принадлежат каждой роли пользователя.<br />
<br />
Результат возвращается в виде <code>json-строки</code> формата:<br />
<pre><br />
{<br />
"": {<br />
"clusterRoles": [...],<br />
"roles": {<br />
"allNamespaces": [...],<br />
"namespaces": [<br />
{<br />
"": [...],<br />
...<br />
}<br />
}<br />
}<br />
}<br />
</pre><br />
Где <code>[...]</code> - массив объектов типа:<br />
<pre><br />
{<br />
"bindRoleName": "",<br />
"bindedRoleType": "ClusterRole|Role",<br />
"bindedRoleName": "",<br />
"unbindCmd": "podsec-k8s-rbac-unbindrole ..."<br />
}<br />
</pre><br />
<br />
== podsec-k8s-rbac-unbindrole - отвязывание пользователя от кластерной или обычной роли ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-unbindrole имя_пользователя role|clusterrole роль имя_связки_роли [namespace]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит отвязку роли от кластерной или обычной роли, созданной командой <code>podsec-k8s-rbac-bindrole</code>. Полный текст команды можно получить в выводе команды <code>podsec-k8s-rbac-get-userroles</code> в поле <code>unbindCmd</code>. Если в указанном имя_связки_роли объекте класса <code>rolebindings</code> или <code>clusterrolebindings</code> еще остаются пользователи - объект модифицируется. Если список становится пуст - объект удаляется.<br />
<br />
'''Параметры''':<br />
<br />
* <code>имя_пользователя</code> должно быть создано командой <code>podsec-k8s-rbac-create-user</code> и сконфигурировано на доступ к кластеру командой <code>podsec-k8s-rbac-create-kubeconfig</code>;<br />
* тип роли может принимать следующие значения:<br />
* <code>role</code> - пользователь привязывается к обычной роли с именем <роль> (параметр <code>namespace</code> в этом случае обязателен);<br />
* <code>clusterrole</code> - пользователь привязывается к кластерной роли используя кластерную роль с именем <роль> (параметр <code>namespace</code> в этом случае должен отсутствовать).<br />
* <code>роль</code> - имя обычной или кластерной роли в зависимости от предыдущего параметра;<br />
* <code>имя_связки_роли</code> - имя объекта класса <code>rolebindings</code> или <code>clusterrolebindings</code> в зависимости от параметра тип роли. В рамках этого объекта к кластерной или обычной роли могут быть привязаны несколько пользователей.<br />
* <code>namespace</code> - имя <code>namespace</code> для обычной роли.<br />
<br />
= podsec-inotify - Мониторинг безопасности системы =<br />
<br />
В пакет podsec-inotify входит набор скриптов для мониторинга безопасности системы: <br />
* podsec-inotify-check-policy - проверка настроек политики контейнеризации на узле;<br />
* podsec-inotify-check-containers - проверка наличия изменений файлов в директориях rootless контейнерах;<br />
* podsec-inotify-check-images - проверка образов на предмет их соответствия настройки политикам контейнеризации на узле;<br />
* podsec-inotify-check-kubeapi - мониторинг аудита API-интерфейса kube-apiserver control-plane узла;<br />
* podsec-inotify-check-vuln - мониторинг docker-образов узла сканером безопасности trivy.<br />
<br />
== podsec-inotify-check-policy - проверка настроек политики контейнеризации на узле ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-policy [-v[vv]] [-a интервал] [-f интервал] -c интервал -h интервал [-m интервал] х-w интервалъ [-l интервал] [-d интервал]</pre><br />
<br />
'''Описание''':<br />
Плугин проверяет настройки политики контейнеризации на узле.<br />
<br />
Проверка идет по следующим параметрам:<br />
<br />
* файл <code>policy.json</code> установки транспортов и политик доступа к регистраторам:<br />
{| class="wikitable"<br />
|+<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| имеющих <code>defaultPolicy != reject</code>, но не входящих в группу <code>podman_dev</code> || 102<br />
|-<br />
| не имеющих не имеющих <code>registry.local</code> в списке регистраторов для которых проверяется наличие электронной подписи образов || 103<br />
|-<br />
| имеющих в политике регистраторы для которых не проверяется наличие электронной подписи образов || 104<br />
|-<br />
| имеющих в списке поддерживаемых транспорты отличные от <code>docker</code> (транспорт получения образов с регистратора) || 105<br />
|}<br />
<br />
<br />
* файлы привязки регистраторов к серверам хранящим электронные подписи (файл привязки о умолчанию <code>default.yaml</code> и файлы привязки регистраторов <code>*.yaml</code> каталога <code>registries.d</code>). Наличие (число) пользователей:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| не использующих хранилище подписей <code>http://sigstore.local:81/sigstore/</code> как хранилище подписей по умолчанию || 106<br />
|}<br />
<br />
* контроль групп пользователей<br />
# наличие пользователей имеющих образы, но не входящих в группу <code>podman</code>:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| наличие пользователей имеющих образы, но не входящих в группу <code>podman</code> || 101<br />
|}<br />
# наличие пользователей группы <code>podman</code> (за исключением входящих в группу <code>podman_dev</code>):<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| входящих в группу <code>wheel</code> || 101<br />
|-<br />
| имеющих каталог <code>.config/containers/</code> открытым на запись и изменения || 90 * <code>доля_нарушителей</code><br />
|-<br />
| не имеющих файла конфигурации <code>.config/containers/storage.conf</code> || 90 * <code>доля_нарушителей</code><br />
|}<br />
<code>доля_нарушителей</code> считается как: <code>число_нарушителей / число_пользователей_группы_podman</code><br />
<br />
Все веса метрик суммируются и формируется итоговая метрика.<br />
<br />
== podsec-inotify-check-containers - проверка наличия изменений файлов в директориях rootless контейнерах ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-containers</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт:<br />
* создаёт список директорий <code>rootless</code> контейнеров, существующих в системе,<br />
* запускает проверку на добавление,удаление, и изменение файлов в директориях контейнеров,<br />
* отсылает уведомление об изменении в системный лог.<br />
<br />
== podsec-inotify-check-images - проверка образов на предмет их соответствия настройки политикам контейнеризации на узле ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-images [-v[vv]] [-a интервал] [-f интервал] -c интервал -h интервал [-m интервал] х-w интервалъ [-l интервал] [-d интервал]</pre><br />
<br />
'''Описание''':<br />
<br />
Плугин проверяет образы на предмет их соответствия настройки политикам контейнеризации на узле. Проверка идет по следующим параметрам:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| наличие в политике пользователя регистраторов не поддерживающие электронную подпись || 101<br />
|-<br />
| наличие в кэше образов неподписанных образов || 101<br />
|-<br />
| наличие в кэше образов вне поддерживаемых политик || 101<br />
|}<br />
Все веса метрик суммируются и формируется итоговая метрика.<br />
<br />
== podsec-inotify-check-kubeapi - мониторинг аудита API-интерфейса kube-apiserver control-plane узла ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-kubeapi [-d]</pre><br />
<br />
'''Описание''':<br />
Скрипт производит мониторинг файла <code>/etc/kubernetes/audit/audit.log</code> аудита API-интерфейса <code>kube-apiserver</code>.<br />
<br />
Политика аудита располагается в файле <code>/etc/kubernetes/audit/policy.yaml</code>:<br />
<pre><br />
apiVersion: audit.k8s.io/v1<br />
kind: Policy<br />
omitManagedFields: true<br />
rules:<br />
# do not log requests to the following <br />
- level: None<br />
nonResourceURLs:<br />
- "/healthz*"<br />
- "/logs"<br />
- "/metrics"<br />
- "/swagger*"<br />
- "/version"<br />
- "/readyz"<br />
- "/livez"<br />
<br />
- level: None<br />
users:<br />
- system:kube-scheduler<br />
- system:kube-proxy<br />
- system:apiserver<br />
- system:kube-controller-manager<br />
- system:serviceaccount:gatekeeper-system:gatekeeper-admin<br />
<br />
- level: None<br />
userGroups:<br />
- system:nodes<br />
- system:serviceaccounts<br />
- system:masters<br />
<br />
# limit level to Metadata so token is not included in the spec/status<br />
- level: Metadata<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: authentication.k8s.io<br />
resources:<br />
- tokenreviews<br />
<br />
# extended audit of auth delegation<br />
- level: RequestResponse<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: authorization.k8s.io<br />
resources:<br />
- subjectaccessreviews<br />
<br />
# log changes to pods at RequestResponse level<br />
- level: RequestResponse<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: "" # core API group; add third-party API services and your API services if needed<br />
resources: ["pods"]<br />
verbs: ["create", "patch", "update", "delete"]<br />
<br />
# log everything else at Metadata level<br />
- level: Metadata<br />
omitStages:<br />
- RequestReceived<br />
</pre> <br />
<br />
Текущие настройки производят логирование всех обращений "несистемных" пользователей (в том числе анонимных) к ресурсам <code>kubernetes</code>.<br />
<br />
Скрипт производит выборку всех обращений, в ответ на которые был сформирован код более <code>400</code> - запрет доступа. <br />
Все эти факты записываются в системный журнал и накапливаются в файле логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code>, который периодически передается через посту системному адмиристратору.<br />
<br />
'''Параметры''':<br />
<br />
* <code>-d</code> - скирпт запускается в режиме демона, производящего онлайн мониторинг файла <code>/etc/kubernetes/audit/audit.log</code> и записывающего факты запросов с запретом доступа в системный журнал и файл логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code>.<br />
<br />
* при запуске без параметров скрипт посылает файл логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code> почтой системному администратору (пользователь <code>root</code>) и обнуляет файл логов.<br />
<br />
В состав пакета кроме этого скрипта входят:<br />
<br />
* файл описания сервиса </code>/lib/systemd/system/podsec-inotify-check-kubeapi.service</code>. Для его запуска екобходимо выполнить команды:<br />
<pre> <br />
# systemctl enable podsec-inotify-check-kubeapi.service<br />
# systemctl start podsec-inotify-check-kubeapi.service<br />
</pre><br />
<br />
* файл для </code>cron</code> </code>/etc/podsec/crontabs/podsec-inotify-check-kubeapi</code>. Файл содержит единственную строку с описанием режима запуска скрипта </code>podsec-inotify-check-kubeapi</code> для передачи почты системному администратору. <br />
Скрипт запускается один раз в 10 минут.<br />
Во время установки пакета строка файла (в случае ее отсутствия) дописыватся в </code>crontab</code>-файл </code>/var/spool/cron/root</code> пользователя </code>root</code>. <br />
Если необходимо изменить режим запуска скрипта или выключить его это можно сделать командой редактирования </code>crontab</code>-файла:<br />
<pre><br />
# crontab -e<br />
</pre><br />
<br />
== podsec-inotify-check-vuln - мониторинг docker-образов узла сканером безопасности trivy ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-vuln</pre><br />
<br />
'''Описание''':<br />
<br />
<br />
<br />
Для корректной работы скрипта необходимо запустить сервис <code>podsec-inotify-server-trivy</code>:<br />
<pre><br />
systemctl enable --now podsec-inotify-server-trivy<br />
</pre><br />
<br />
Скрипт производит мониторинг <code>docker-образов</code> узла сканером безопасности <code>trivy</code>:<br />
<br />
* Если скрипт запускается от имени пользователя <code>root</code> скрипт:<br />
# проверяет сканером <code>trivy</code> <code>rootfull</code> образы;<br />
# для всех пользователей каталога <code>/home/</code> проверяется наличие <code>rootless</code>-образов. При их наличии проверяет сканером <code>trivy</code> эти образы.<br />
<br />
* Если скрипт запускается от имени обычного пользователя проверяется наличие <code>rootless</code>-образов. При их наличии проверяет сканером <code>trivy</code> эти образы.<br />
<br />
Результат анализа посылается в системный лог.<br />
Если при анализе образа число обнаруженных угроз уровня <code>HIGH</code> больше 0, результат посылается почтой системному администратору (<code>root</code>).<br />
<br />
'''Параметры''':<br />
<br />
Отсутствуют.<br />
<br />
В состав пакета кроме этого скрипта входит файл для <code>cron</code> <code>/etc/podsec/crontabs/podsec-inotify-check-vuln</code>. Файл содержит единственную строку с описанием режима запуска скрипта <code>podsec-inotify-check-vuln</code>.<br />
Во время установки пакета строка файла (в случае ее отсутствия) дописыватся в <code>crontab</code>-файл <code>/var/spool/cron/root</code> пользователя <code>root</code>.<br />
<br />
Если необходимо изменить режим запуска скрипта или выключить его это можно сделать командой редактирования <code>crontab</code>-файла:<br />
<pre><br />
# crontab -e<br />
</pre></div>Kafhttps://www.altlinux.org/index.php?title=Rootless_kubernetes&diff=78990Rootless kubernetes2024-03-25T16:52:14Z<p>Kaf: /* Указании версии основных kubernetes образов */</p>
<hr />
<div><br />
<br />
= Общее описание =<br />
<br />
Запуск <code>Kubernetes</code> в режиме <code>rootless</code> обеспечивает запуск <code>Pod</code>ов без системных <code>root</code>-привелегий в рамках <code>user namespace</code> системного пользователя <code>u7s-admin</code>. Работа в этом режиме практически не требует никаких модификаций, но обеспечивает повышенные уровень защищенности <code>kubernetes</code>, так как клиентские приложения даже при использовании уязвимостей не могут получить права пользователя <code>root</code> и нарушить работу узла.<br />
<br />
Запуск <code>kubernetes</code> версии <code>1.26.3</code> и старше в режиме <code>rootless</code> обеспечивает пакет <code>podsec-k8s</code> версии <code>1.0.5</code> или выше.<br />
<br />
'''Для разворачивания <code>rootless kubernetes</code> требуются ядра ядрах ''5.15'' и выше.'''<br />
<br />
= podsec-k8s - Быстрый старт =<br />
<br />
== Установка master-узла ==<br />
<br />
=== Инициализация master-узла ===<br />
<br />
Для запуска <code>kubernetes</code> в режиме <code>rootless</code> установите пакет <code>podsec-k8s</code> версии <code>1.0.5</code> или выше.<br />
<pre>apt-get install podsec-k8s</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
В каталоге <code>/usr/libexec/podsec/u7s/bin/</code> находятся программы, обеспечивающие работы <code>kubernetes</code><br />
в <code>rootless</code>-режиме.<br />
<br />
Для разворачивания <code>master-узла</code> запустите команду:<br />
<br />
<pre>kubeadm init</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>init</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
После:<br />
<br />
* генерации сертификатов в каталоге <code>/etc/kubernetes/pki</code>,<br />
* загрузки образов, <br />
* генерации conf-файлов в каталоге <code>/etc/kubernetes/manifests/</code>, <code>/etc/kubernetes/manifests/etcd/</code><br />
* запуска сервиса <code>kubelet</code> и <code>Pod</code>’ов системных <code>kubernetes-образов</code><br />
<br />
инициализируется <code>kubernet-кластер</code> из одного узла.<br />
<br />
По окончании скрипт выводит строки подключения <code>master</code>(<code>Control Plane</code>) и <code>worker-узлов</code>:<br />
<pre><br />
You can now join any number of control-plane nodes by copying certificate authorities<br />
and service account keys on each node and then running the following as root:<br />
<br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:.. --control-plane<br />
<br />
Then you can join any number of worker nodes by running the following on each as root:<br />
<br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...<br />
</pre><br />
<br />
=== Запуск сетевого маршрутизатора для контейенеров kube-flannel ===<br />
<br />
Для версии <code>podsec 1.0.8</code> и выше этот шаг выполнять не надо - он выполняется во время <code>kubeadm init</code>. <br />
<br />
Для перевода узла в состояние <code>Ready</code>, запуска <code>coredns</code> <code>Pod</code>’ов запустите <code>flannel</code>.<br />
<br />
На <code>master-узле</code> под пользоваталем <code>root</code> выполните команду:<br />
<br />
<pre># kubectl apply -f /etc/kubernetes/manifests/kube-flannel.yml<br />
Connected to the local host. Press ^] three times within 1s to exit session.<br />
[INFO] Entering RootlessKit namespaces: OK<br />
namespace/kube-flannel created<br />
clusterrole.rbac.authorization.k8s.io/flannel created<br />
clusterrolebinding.rbac.authorization.k8s.io/flannel created<br />
serviceaccount/flannel created<br />
configmap/kube-flannel-cfg created<br />
daemonset.apps/kube-flannel-ds created<br />
Connection to the local host terminated.</pre><br />
После завершения скрипта в течении минуты настраиваются сервисы мастер-узла кластера. По ее истечении проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
=== Проверка работы master-узла ===<br />
<br />
На <code>master-узле</code> выполните команду:<br />
<br />
<pre># kubectl get daemonsets.apps -A<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE<br />
kube-flannel kube-flannel-ds 1 1 1 1 1 &lt;none&gt; 102s<br />
kube-system kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 8h</pre><br />
Число <code>READY</code> каждого <code>daemonset</code> должно быть равно числу <code>DESIRED</code> и должно быть равно числу узлов кластера.<br />
<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host> Ready control-plane 16m v1.26.3 10.96.0.1 <none> ALT SP Server 11100-01 5.15.105-un-def-alt1 cri-o://1.26.2<br />
</pre><br />
Проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
<pre><br />
# kubectl get all -A<br />
NAMESPACE NAME READY STATUS RESTARTS AGE<br />
kube-system pod/coredns-c7df5cd6c-5pkkm 1/1 Running 0 19m<br />
kube-system pod/coredns-c7df5cd6c-cm6vf 1/1 Running 0 19m<br />
kube-system pod/etcd-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-apiserver-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-controller-manager-host-212 1/1 Running 0 19m<br />
kube-system pod/kube-proxy-lqf9c 1/1 Running 0 19m<br />
kube-system pod/kube-scheduler-host-212 1/1 Running 0 19m<br />
<br />
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE<br />
default service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19m<br />
kube-system service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 19m<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE<br />
kube-system daemonset.apps/kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 19m<br />
<br />
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE<br />
kube-system deployment.apps/coredns 2/2 2 2 19m<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY AGE<br />
kube-system replicaset.apps/coredns-c7df5cd6c 2 2 2 19m<br />
</pre><br />
Состояние всех <code>Pod</code>’ов должны быть в <code>1/1</code>.<br />
<br />
Проверьте состояние дерева <code>rootless-процессов</code>:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─nsenter.sh───nsenter───_kubelet.sh───kubelet───11*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-controller───7*[{kube-controller}]<br />
│ │ ├─conmon───kube-apiserver───8*[{kube-apiserver}]<br />
│ │ ├─conmon───kube-scheduler───7*[{kube-scheduler}]<br />
│ │ ├─conmon───etcd───8*[{etcd}]<br />
│ │ ├─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─2*[conmon───coredns───8*[{coredns}]]<br />
│ │ ├─rootlesskit.sh───crio───10*[{crio}]<br />
│ │ └─7*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
...<br />
</pre><br />
Процесс <code>kubelet</code> запускается как сервис в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
Все остальные процессы <code>kube-controller</code>, <code>kube-apiserver</code>, <code>kube-scheduler</code>, <code>kube-proxy</code>, <code>etcd</code>, <code>coredns</code> запускаются как контейнеры от соответствующих образов в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
=== Обеспечение запуска обычных POD’ов на мастер-узле ===<br />
<br />
По умолчанию на master-узле пользовательские <code>Pod</code>ы не запускаются. Чтобы снять это ограничение наберите команду:<br />
<br />
<pre># kubectl taint nodes &lt;host&gt; node-role.kubernetes.io/control-plane:NoSchedule-<br />
node/&lt;host&gt; untainted</pre><br />
== Инициализация и подключение worker-узла ==<br />
<br />
Установите пакет <code>podsec-k8s</code>:<br />
<pre><br />
apt-get install podsec-k8s<br />
</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
<br />
=== Подключение worker-узлов ===<br />
<br />
Скопируйте команду подключения <code>worker-узла</code>, полученную на этапе установки начального <code>master-узла</code>. Запустите ее:<br />
<br />
<pre>kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>join</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
По окончании скрипт выводит текст:<br />
<pre><br />
This node has joined the cluster:<br />
* Certificate signing request was sent to apiserver and a response was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
<br />
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.<br />
</pre><br />
<br />
=== Проверка состояния процессов ===<br />
<br />
Проверьте состояние дерева <code>rootless-процессов</code>:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─nsenter.sh───nsenter───_kubelet.sh───kubelet───10*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─rootlesskit.sh───crio───9*[{crio}]<br />
│ │ └─6*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
...<br />
</pre><br />
Процесс <code>kubelet</code> запускается как сервис в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
Все остальные процессы <code>kube-proxy</code>, <code>kube-flannel</code> запускаются как контейнеры от соответствующих образов в <code>user namespace</code> процесса <code>rootlesskit</code>.<br />
<br />
=== Проверка готовности master и worker узлов kubernets ===<br />
<br />
Зайдите на <code>master-узел</code> и проверьте подключение <code>worker-узла</code>:</li></ol><br />
<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host1> Ready control-plane 7h54m v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
&lt;host2> Ready &lt;none&gt; 8m30s v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
...<br />
</pre><br />
<br />
== Инициализация и подключение дополнительных master-узлов ==<br />
<br />
Установите пакет <code>podsec-k8s</code>:<br />
<pre><br />
apt-get install podsec-k8s<br />
</pre><br />
Измените переменную PATH:<br />
<pre>export PATH=/usr/libexec/podsec/u7s/bin/:$PATH</pre><br />
<br />
=== Подключение master-узлов ===<br />
<br />
Скопируйте команду подключения <code>master-узла</code>, полученную на этапе установки начального <code>master-узла</code>. <br />
Она отличается от команды подключения <code>worker</code>-узлов наличием дополнительных параметров<br />
<code>--control-plane</code>, <code>--certificate-key</code>.<br />
<br />
Запустите ее:<br />
<pre><br />
kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...<br />
--control-plane --certificate-key ... <br />
</pre><br />
<blockquote>По умолчанию уровень отладки устанавливается в <code>0</code>. Если необходимо увеличить уровень отладки укажите перед подкомандой <code>join</code> флаг <code>-v n</code>. Где <code>n</code> принимает значения от <code>0</code> до <code>9</code>-ти.<br />
</blockquote><br />
<br />
По окончании скрипт выводит текст:<br />
<pre><br />
This node has joined the cluster and a new control plane instance was created:<br />
<br />
* Certificate signing request was sent to apiserver and approval was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
* Control plane label and taint were applied to the new node.<br />
* The Kubernetes control plane instances scaled up.<br />
* A new etcd member was added to the local/stacked etcd cluster.<br />
</pre><br />
<br />
=== Проверка состояния процессов ===<br />
<br />
Проверьте состояние дерева процессов:<br />
<pre><br />
# pstree<br />
...<br />
├─systemd─┬─(sd-pam)<br />
│ ├─dbus-daemon<br />
│ ├─kubelet.sh───nsenter_u7s───nsenter───_kubelet.sh───kubelet───11*[{kubelet}]<br />
│ └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-controller───4*[{kube-controller}]<br />
│ │ ├─conmon───kube-scheduler───8*[{kube-scheduler}]<br />
│ │ ├─conmon───etcd───9*[{etcd}]<br />
│ │ ├─conmon───kube-proxy───4*[{kube-proxy}]<br />
│ │ ├─conmon───kube-apiserver───8*[{kube-apiserver}]<br />
│ │ ├─rootlesskit.sh───crio───8*[{crio}]<br />
│ │ └─7*[{exe}]<br />
│ ├─slirp4netns<br />
│ └─8*[{rootlesskit}]<br />
</pre><br />
<br />
Дерево <code>rootless-процессов</code> должно отличаться от дерева процессов основного <code>master-узла</code><br />
отсутствием контейнеров <code>coredns</code>.<br />
<br />
=== Проверка готовности master и worker узлов kubernets ===<br />
<br />
На одном из master-узлов наберите команду:<br />
<pre><br />
# kubectl get nodes -o wide<br />
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME<br />
&lt;host1> Ready control-plane 7h54m v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
&lt;host2> Ready &lt;none&gt; 8m30s v1.26.3 10.96.0.1 &lt;none&gt; ALT cri-o://1.26.2<br />
...<br />
&lt;hostN> Ready control-plane 55m v1.26.3 10.96.122.&lt;N> <none> ALT cri-o://1.26.2<br />
...<br />
</pre><br />
<br />
=== Использование REST-интерефейсов подключенных master-узлов ===<br />
<br />
По умолчанию на подключенных <code>master-узлах</code> в файле <code>/etc/kubernetes/admin.conf</code> <br />
указан адрес <code>API-интерфейса</code> основного <code>master-узла</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://<master1>:6443<br />
...<br />
</pre> <br />
<br />
Для балансировки нагрузки в файлах конфигурации <code>~user/.kube/config</code> <br />
есть смысл указать адреса <code>API-интерфейсов</code> дополнительных <code>master-узлов</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://<masterN>:6443<br />
...<br />
</pre><br />
<br />
== Создание гетерогенных кластеров, миграция с rootfull кластеров на rootless кластера ==<br />
<br />
В рамках одного кластера могут функционировать как <code>rootfull</code> узла`, так и <code>rootless узлы</code>.<br />
Например имеет смысл в расках <code>rootfull</code> кластера для повышения защищенности кластера <br />
подключать в качестве <code>Worker</code>оа <code>rootless</code> узлы.<br />
<br />
Перед подключением `rootless` узлов необходимо выполнить определенные действия.<br />
<br />
=== Запуск kube-proxy на rootless-узле в rootfull кластере ===<br />
<br />
Для запуска <code>kube-proxy</code> на <code>rootless-узле</code> в <code>rootfull кластере</code> на <code>ControlPlane</code> узле:<br />
<br />
* Наберите команду редактирования <code>ConfigMap</code>а <code>kube-proxy</code>:<br />
<pre><br />
kubectl -n kube-system edit Configmaps kube-proxy<br />
</pre><br />
<br />
* Измение в элементе <code>data.config.conf</code> значение переменной <code>conntrack.maxPerCore</code> с <code>null</code> на <code>0</code>.<br />
<br />
* Выйдите из редактора<br />
<br />
=== Запуск ControlPlane узла на rootless-узле в rootfull кластере ===<br />
<br />
Для запуска <code>ControlPlane</code> на <code>rootless-узле</code> в <code>rootfull кластере</code> на <code>ControlPlane</code> узле:<br />
<br />
* Наберите команду редактирования <code>ConfigMap</code>а <code>kubeadm-config</code>:<br />
<pre><br />
kubectl -n kube-system edit Configmaps kubeadm-config<br />
</pre><br />
<br />
* Измение в элементе <code>data.ClusterConfiguration</code> значение переменной <code>etcd.local.dataDir</code> с <code>/var/lib/etcd</code> на <code>/var/lib/podsec/u7s/etcd</code>.<br />
<br />
* Выйдите из редактора<br />
<br />
== Получениe строки подключения узла к кластеру ==<br />
<br />
=== Получении строки подключения Worker узла к кластеру ===<br />
<br />
В случае, если команда строки подключения утеряна или срок сгенерированного сертификата<br />
истек можно сгенерировать новую строку подключения, выполнив команду:<br />
<pre><br />
joinCommand=$(/usr/bin/kubeadm token create --print-join-command)<br />
</pre><br />
и выполнить команду подключения:<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
$joinCommand <br />
</pre><br />
<br />
=== Получении строки подключения Control-plane узла к кластеру ===<br />
<br />
В определенных случаях `kubeadm init` генерирует только строку подключения `worker` узлов.<br />
Или срок действия сертификата для подключения истек.<br />
<br />
В этом случае есть смысл перегенерировать сертификат:<br />
<pre><br />
cert=$(/usr/bin/kubeadm init phase upload-certs --upload-certs 2>/dev/null | tail -1)<br />
</pre><br />
строку подключения `control-plane` и `worker` узлов к кластеру:<br />
<pre><br />
joinCommand=$(/usr/bin/kubeadm token create --print-join-command)<br />
</pre><br />
и выполнить команду подключения:<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
$joinCommand --control-plane --certificate-key $cert<br />
</pre><br />
<br />
См. [https://stackoverflow.com/questions/51126164/how-do-i-find-the-join-command-for-kubeadm-on-the-master How do I find the join command for kubeadm on the master?]<br />
<br />
== Системный пользователь u7s-admin ==<br />
<br />
Все контейнеры в <code>rootless kubernetes</code>. включая системные работают от имени системного пользователя <code>u7s-admin</code>.<br />
Вы можете для мониторинга работы системы или запуска дополнительного функционала работать в системе от имени этого пользователя.<br />
<br />
Для входа в терминальный режим этого пользователя достаточно в пользователе с правами <code>root</code> набрать команду:<br />
<pre><br />
# machinectl shell u7s-admin@ /bin/bash<br />
</pre><br />
или задав пароль пользователя:<br />
<pre><br />
# passwd u7s-admin<br />
</pre> <br />
зайти в него через <code>ssh</code>.<br />
<br />
Для входа в namespace пользователя наберите команду :<br />
<pre><br />
$ nsenter_u7s<br />
#<br />
</pre><br />
<br />
В рамках своего <code>namespace</code> пользователь <code>u7s-admin</code> имеет права <code>root</code>, оставаясь в рамках системы <br />
с правам пользователя <code>u7s-admin</code>.<br />
<br />
Наличие прав <code>root</code> позволает использовать системные команды,требующих <code>root-привелегий</code> для работы с сетевым, файловым окружением (эти окружения отличаются от системных): <code>ip</code>, <code>iptables</code>, <code>crictl</code>, ...<br />
<br />
С помощью команды <code>crict</code>l можно <br />
* посмотреть наличие образов в системном кэше, <br />
* удалить, загрузить образы<br />
* посмотреть состояние контейнеров, pod'ов<br />
* и т.п. <br />
<br />
Кроме этого <code>namespace</code> пользователя <code>u7s-admin</code> присутствуют файлы и каталоге созданные в рамках данного <br />
<code>namespace</code> и отсутствующие в основной системе.<br />
Например Вы можете посмотреть логи контейнеров в каталоге <code>/var/log/pods</code> и т.п.<br />
<br />
== Особенности разворачивания приложений в rootless kubernetes ==<br />
<br />
При использовании сервисов типа <code>NodePort</code> поднятые в рамках кластера порты в диапазоне <code>30000-32767</code> остаются в <code>namespace</code> пользователя <code>u7s-admin</code>. Для их проброса наружу необходимо в пользователе <code>u7s-admin</code> запустить команду:<br />
<pre><br />
$ nsenter_u7s rootlessctl add-ports 0.0.0.0:&lt;port>:&lt;port>/tcp<br />
</pre><br />
Сервисы типа <code>NodePort</code> из за их небольшого диапазона и "нестабильности" портов при переносе решения в другой кластер довольно редко используются. Рекомендуется вместо них использовать сервисы типа <code>ClusterIP</code> c доступом к ним через <code>Ingress</code>-контроллеры.<br />
<br />
= Разворачивание rootless kubernetes кластера с балансировщиком REST-запросов haproxy =<br />
<br />
Вышеописанный процесс разворачивания обеспечивать только ручную балансировку запросов:<br />
[[Файл:Variant1.drawio.png|840px||center|rootless kubernetes-кластер без балансировщика haproxy]]<br />
<br />
Ручная балансировка запросов к <code>API-интерфейсам</code> <code>master-узлов</code> путем указания у клиентов адресов различных <br />
<code>master-узлов</code> довольно неудобна, так как не обеспечивает равномерного распределения запросов по узлам кластера и не обеспечивает автоматической отказоустойчивости при выходе из строя <code>master-узлов</code>.<br />
<br />
Решает данную проблему установка балансировщика нагрузки <code>haproxy</code>. <br />
[[Файл:Variant_haproxy_1.drawio.png|840px||center|rootless kubernetes-кластер без балансировщика haproxy]]<br />
<br />
Перевод кластера в режим балансировки запросов через haproxy возможен.<br />
Подробности описаны в статье [https://stackoverflow.com/questions/65505137/how-to-convert-a-kubernetes-non-ha-control-plane-into-an-ha-control-plane How to convert a Kubernetes non-HA control plane into an HA control plane?], но данная процедура не гарантирует корректный перевод на всех версиях <code>kubernetes</code> и ее не рекомендуют применять на <code>production</code> кластерах.<br />
<br />
Так что наиболее надежным способом создания кластера с балансировкой запросов является создание нового кластера. <br />
<br />
== Настройка балансировщика REST-запросов haproxy ==<br />
<br />
Балансировщик <code>REST-запросов haproxy</code> можно устанавливать как на отдельный сервер, так на один из серверов кластера.<br />
[[Файл:Variant haproxy master.drawio.png|840px|center|безрамки]]<br />
<br />
<blockquote><br />
'''Если балансировщик устанавливается на <code>rootless</code> сервер кластера, то для балансировщика необходимо выделить отдельный IP-адрес. Если на этом же сервере функционируют локальный регистратор (<code>registry.local</code>) и сервер подписей (<code>sigstore.local</code>), то IP-адрес балансировщика может совпадать c IP-адресами этих сервисов. <br />
''' <br />
</blockquote><br />
<br />
<blockquote><br />
'''Если планируется создание отказоустойчивого решения на основе нескольких серверов <code>haproxy</code>, то для них кроме собственного <code>IP-адреса</code> необходимо будет для всех серверов <code>haproxy</code> выделить один общий <code>IP-адрес</code>, который будет иметь <code>master-балансировщик</code>.<br />
''' <br />
</blockquote><br />
<br />
<br />
Полная настройка отказоустойчивого кластера <code>haproxy</code> из 3-х узлов описана в документе [https://www.altlinux.org/ALT_Container_OS_%D0%BF%D0%BE%D0%B4%D0%B2%D0%B5%D1%82%D0%BA%D0%B0_K8S._%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5_HA_%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B0 ALT Container OS подветка K8S. Создание HA кластера].<br />
<br />
Здесь же мы рассмотрим создание и настройка с одним сервером <code>haproxy</code> с балансировкой запросов на <code>master</code>-узлы.<br />
<br />
Установите пакет <code>haproxy</code>:<br />
<br />
<pre># apt-get install haproxy</pre><br />
Отредактируйте конфигурационный файл <code>/etc/haproxy/haproxy.cfg</code>:<br />
<br />
<ul><br />
<li>добавьте в него описание <code>frontend</code>’a <code>main</code>, принимающего запросы по порту <code>8443</code>:<br />
<pre><br />
frontend main<br />
bind *:8443<br />
mode tcp<br />
option tcplog<br />
default_backend apiserver<br />
</pre></li><br />
<li>добавьте описание <code>backend</code>’а <code>apiserver</code>:<br />
<pre><br />
backend apiserver<br />
option httpchk GET /healthz<br />
http-check expect status 200<br />
mode tcp<br />
option ssl-hello-chk<br />
balance roundrobin<br />
server master01 &lt;IP_или_DNS_начального_мастер_узла>:6443 check<br />
</pre></li><br />
<li>запустите <code>haproxy</code>:</li></ul><br />
<br />
<pre># systemctl enable haproxy<br />
# systemctl start haproxy</pre><br />
<br />
== Инициализация master-узла ==<br />
<br />
==== Инициализация мастер-узла при работа с балансировщиков haproxy ====<br />
<br />
При установке начального master-узла необходимо параметром <code>control-plane-endpoint</code> указать URL балансировщика <code>haproxy</code>:<br />
<br />
<pre># kubeadm init --apiserver-advertise-address 192.168.122.80 --control-plane-endpoint &lt;IP_адрес_haproxy&gt;:8443</pre><br />
При запуске в параметре <code>--apiserver-advertise-address</code> укажите IP-адрес API-интерфейса <code>kube-apiserver</code>.<br />
<br />
'''IP-адреса в параметрах''' <code>--apiserver-advertise-address</code> '''и''' <code>--control-plane-endpoint</code> '''должны отличаться. Если Вы развернули''' <code>haproxy</code> '''на том же мастер-узле, поднимите на сетевом нтерфейсе дополнительный IP-адрес и укажите его в параметре''' <code>--control-plane-endpoint</code>'''.<br />
<br />
В результате инициализации <code>kubeadm</code> выведет команды подключения дополнительных <code>control-plane</code> и <code>worker</code> узлов:<br />
<pre><br />
...<br />
You can now join any number of the control-plane node running the following command on each as root:<br />
<br />
kubeadm join &lt;IP_адрес_haproxy>:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:... \<br />
--control-plane --certificate-key ...<br />
<br />
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!<br />
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use<br />
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.<br />
<br />
Then you can join any number of worker nodes by running the following on each as root:<br />
<br />
kubeadm join <IP_адрес_haproxy>:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:...<br />
...<br />
</pre><br />
Обратите внимание - в командах присоединения узлов указывается не URL созданного начального master-узла (<code>&lt;IP_или_DNS_начального_мастер_узла&gt;:6443</code>), а URL <code>haproxy</code>.<br />
<br />
В сформированных файлах конфигурации <code>/etc/kubernetes/admin.conf</code>, <code>~/.kube/config</code> также указывается URL <code>haproxy</code>:<br />
<pre><br />
apiVersion: v1<br />
clusters:<br />
- cluster:<br />
...<br />
server: https://&lt;IP_адрес_haproxy>:8443<br />
</pre><br />
То есть вся работа с кластеров в дальнейшем идет через балансировщик запросов <code>haproxy</code>.<br />
<br />
Для перевода узла в состояние <code>Ready</code>, запуска coredns Pod’ов запустите flannel<br />
<br />
==== Запуск сетевого маршрутизатора для контейнеров kube-flannel ====<br />
<br />
На <code>master-узле</code> под пользоваталем <code>root</code> выполните команду:<br />
<br />
<pre># kubectl apply -f /etc/kubernetes/manifests/kube-flannel.yml<br />
Connected to the local host. Press ^] three times within 1s to exit session.<br />
[INFO] Entering RootlessKit namespaces: OK<br />
namespace/kube-flannel created<br />
clusterrole.rbac.authorization.k8s.io/flannel created<br />
clusterrolebinding.rbac.authorization.k8s.io/flannel created<br />
serviceaccount/flannel created<br />
configmap/kube-flannel-cfg created<br />
daemonset.apps/kube-flannel-ds created<br />
Connection to the local host terminated.</pre><br />
После завершения скрипта в течении минуты настраиваются сервисы мастер-узла кластера. По ее истечении проверьте работу <code>usernetes</code> (<code>rootless kuber</code>)<br />
<br />
== Подключение дополнительных master-узлов ==<br />
<br />
<br />
=== Установка тропы PATH поиска исполняемых команд ===<br />
<br />
Измените переменную <code>PATH</code>:<br />
<br />
<pre><br />
export PATH=/usr/libexec/podsec/u7s/bin/:$PATH<br />
</pre><br />
<br />
=== Подключение master (control plane) узла ===<br />
<br />
Скопируйте строку подключения <code>control-plane</code> узла и вызовите ее:<br />
<br />
<pre># kubeadm join &lt;IP_адрес_haproxy&gt;:8443 --token ... \<br />
--discovery-token-ca-cert-hash sha256:... \<br />
--control-plane --certificate-key ...</pre><br />
В результате работы команда kubeadm выведет строки:<br />
<pre><br />
This node has joined the cluster and a new control plane instance was created:<br />
<br />
* Certificate signing request was sent to apiserver and approval was received.<br />
* The Kubelet was informed of the new secure connection details.<br />
* Control plane label and taint were applied to the new node.<br />
* The Kubernetes control plane instances scaled up.<br />
* A new etcd member was added to the local/stacked etcd cluster.<br />
...<br />
Run 'kubectl get nodes' to see this node join the cluster.<br />
</pre><br />
Наберите на вновь созданном (или начальном)<code>control-plane</code> узле команду:<br />
<br />
<pre># kubectl get nodes<br />
NAME STATUS ROLES AGE VERSION<br />
&lt;host1&gt; Ready control-plane 4m31s v1.26.3<br />
&lt;host2&gt; Ready control-plane 26s v1.26.3<br />
</pre><br />
Обратите внимание, что роль (ROLES) обоих узлов - <code>control-plane</code>.<br />
<br />
Наберите команду:<br />
<pre><br />
# kubectl get all -A<br />
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
kube-flannel pod/kube-flannel-ds-2mhqg 1/1 Running 0 153m 10.96.0.1 <host1> <none> <none><br />
kube-flannel pod/kube-flannel-ds-95ht2 1/1 Running 0 153m 10.96.122.68 <host2> <none> <none><br />
...<br />
kube-system pod/etcd-<host1> 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/etcd-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-apiserver-<host1> 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-apiserver-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-controller-manager-<host1> 1/1 Running 1 (170m ago) 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-controller-manager-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-proxy-9nbxz 1/1 Running 0 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-proxy-bnmd7 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
<br />
kube-system pod/kube-scheduler-<host1> 1/1 Running 1 (170m ago) 174m 10.96.0.1 <host1> <none> <none><br />
kube-system pod/kube-scheduler-<host2> 1/1 Running 0 170m 10.96.122.68 <host2> <none> <none><br />
...<br />
<br />
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR<br />
kube-flannel daemonset.apps/kube-flannel-ds 2 2 2 3 3 <none> 153m kube-flannel registry.local/k8s-c10f1/flannel:v0.19.2 app=flannel<br />
kube-system daemonset.apps/kube-proxy 2 2 2 2 2 kubernetes.io/os=linux 174m kube-proxy registry.local/k8s-c10f1/kube-proxy:v1.26.3 k8s-app=kube-proxy<br />
...<br />
</pre><br />
Убедитесь, что сервисы <code>pod/etcd</code>, <code>kube-apiserver</code>, <code>kube-controller-manager</code>, <code>kube-scheduler</code>, <code>kube-proxy</code>, <code>kube-flannel</code> запустились на обоих control-plane узлах.<br />
<br />
=== Добавление master-узла в балансироващик haproxy ===<br />
<br />
Для балансировки запросов по двум серверам добавьте URL подключенного <code>control-plane</code> узла в файл конфигурации <code>/etc/haproxy/haproxy.cfg</code>:<br />
<pre><br />
backend apiserver<br />
option httpchk GET /healthz<br />
http-check expect status 200<br />
mode tcp<br />
option ssl-hello-chk<br />
balance roundrobin<br />
server master01 &lt;IP_или_DNS_начального_мастер_узла>:6443 check<br />
server master02 &lt;IP_или_DNS_подключенного_мастер_узла>:6443 check<br />
</pre><br />
и перезапустите <code>haproxy</code>:<br />
<br />
<pre># systemctl restart haproxy</pre><br />
Логи обращений и балансировку запросов между узлами можно посмотреть командой:<br />
<br />
<pre># tail -f /var/log/haproxy.log</pre><br />
<br />
== Подключение worker-узлов ==<br />
<br />
Подключение дополнительных worker-узлов происходит аналогично описанному выше в главе '''Инициализация и подключение worker-узла'''.<br />
<br />
== Настройка отказоустойчивого кластера серверов haproxy, keepalived ==<br />
<br />
=== Масштабирование haproxy, установка пакетов ===<br />
<br />
Если необходимо создать отказоустойчивое решение допускающее выход <code>haproxy</code>-севрера из строя <br />
установите <code>haproxy</code> на несколько серверов. Файлы конфигурации <code>haproxy<.code> на всех сервервх должны быть идентичны.<br />
<br />
Для контроля доступности <code>haproxy</code> и переназначений виртуального адреса дополнительно установите на каждом сервис <code>keepalived</code>:<br />
<pre><br />
# apt-get install haproxy keepalived<br />
</pre><br />
<br />
=== Конфигурирование keepalived ===<br />
<br />
[[Файл:Variant haproxy keepalived.png|840px|безрамки|центр|kubeenetes кластер с haproxy и keepalived]]<br />
<br />
Создайте файл конфигурации 'keepalived' ''/etc/keepalived/keepalived.conf'':<br />
<br />
! /etc/keepalived/keepalived.conf<br />
! Configuration File for keepalived<br />
global_defs {<br />
router_id LVS_K8S<br />
}<br />
vrrp_script check_apiserver {<br />
script "/etc/keepalived/check_apiserver.sh"<br />
interval 3<br />
weight -2<br />
fall 10<br />
rise 2<br />
}<br />
<br />
vrrp_instance VI_1 {<br />
state MASTER<br />
interface br0<br />
virtual_router_id 51<br />
priority 101<br />
authentication {<br />
auth_type PASS<br />
auth_pass 42<br />
}<br />
virtual_ipaddress {<br />
10.150.0.160 <br />
}<br />
track_script {<br />
check_apiserver<br />
}<br />
}<br />
<br />
На одном из узлов установите параметр ''state'' в значение ''MASTER'' и параметр ''priority'' в значение ''101''.<br />
На остальных параметр ''state'' в значение ''BACKUP'' и параметр ''priority'' в значение ''100''.<br />
<br />
Скрипт ''/etc/keepalived/check_apiserver.sh'' проверяет доступность балансировщика ''haproxy'':<br />
#!/bin/sh<br />
<br />
errorExit() {<br />
echo "*** $*" 1>&2<br />
exit 1<br />
}<br />
<br />
APISERVER_DEST_PORT=8443<br />
APISERVER_VIP=10.150.0.160<br />
curl --silent --max-time 2 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/"<br />
if ip addr | grep -q ${APISERVER_VIP}; then<br />
curl --silent --max-time 2 --insecure https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/"<br />
fi<br />
<br />
Параметр ''APISERVER_DEST_PORT'' задает порт балансировщиков ''haproxy'', параметр ''APISERVER_VIP'' виртуальный адрес,<br />
через который будут взаимодействовать ''master'' (''control plane'') узлы кластера ''k8s''.<br />
<br />
Скрипт проверяет работоспособность ''haproxy'' на локальной машине.<br />
<br />
Подробности см. на [[https://www.altlinux.org/Keepalived]]<br />
А если в настоящее время виртуальный адрес принадлежит текущему узлу, то и работоспособность ''haproxy'' через виртуальный адрес. <br />
<br />
Добавьте флаг на выполнение скрипта:<br />
chmod a+x /etc/keepalived/check_apiserver.sh<br />
<br />
При работающем балансировщике и хотя бы одному доступному порту ''6443'' на ''master-узлах'' скрипт<br />
должен завершаться с кодом ''0''.<br />
<br />
Подробности см. на [[Keepalived]]<br />
<br />
= Установка и настройка ingress-контролера =<br />
<br />
<code>Ingress-контроллер</code> обеспечивает переадресацию <code>http(s)</code> запросов по указанным шаблонам на внутренние сервисы <code>kubernetes-кластера</code>. <br />
Для <code>bare-metal</code> решений и решений на основе виртуальных машин наиболее приемлимым является<br />
[https://github.com/kubernetes/ingress-nginx ingress-nginx контроллер].<br />
<br />
При применении <code>Ingress-контроллера</code> нет необходимости создавать <code>Nodeport-порты</code> и пробрасывать их из <code>namespace</code> пользователя <code>u7s-admin</code>. <code>Ingress-контроллер</code> переадресует <code>http{s)</code> запрос через сервис непосредственно на порты <code>Pod</code>'ов входящих в реплики <code>deployment</code>.<br />
<br />
== Установка и настройка ingress-nginx-контролера в кластере ==<br />
<br />
[[Файл:Ingress.png|840px|безрамки|центр|Использование ingress-контроллера]]<br />
Для установки <code>Ingress-контроллера</code> скопируйте его YAML-манифест:<br />
<pre><br />
curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.0/deploy/static/provider/baremetal/deploy.yaml -o ingress-nginx-deploy.yaml<br />
</pre><br />
<br />
Выберите свободный порт в диапазона <code>30000 - 32767</code> (например <code>31000</code>) и добавьте его в элемент <br />
<code>spec.ports.appProtocol==http</code><br />
Yaml-описании <code>kind==Service</code>:<br />
<pre><br />
...<br />
---<br />
kind: Service<br />
spec:<br />
ports:<br />
- appProtocol: http<br />
...<br />
nodePort: 31000<br />
...<br />
</pre><br />
Если в Вашем решении используется ТОЛЬКО локальный регистратор <code>registry.local</code> <br />
* создайте алиасы образам nginx:<br />
<pre><br />
podman tag registry.k8s.io/ingress-nginx/controller:v1.8.0@sha256:744ae2afd433a395eeb13dc03d3313facba92e96ad71d9feaafc85925493fee3 registry.local/ingress-nginx/controller:v1.8.0<br />
podman tag registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407@sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b registry.local/ingress-nginx/kube-webhook-certgen:v20230407<br />
</pre><br />
и поместите их в локальный регистратор:<br />
<pre><br />
podman push --tls-verify=false --sign-by='<EMAIL>' registry.local/ingress-nginx/controller<br />
podman push --tls-verify=false --sign-by='<EMAIL>' registry.local/ingress-nginx/kube-webhook-certgen<br />
</pre><br />
* исправьте имена образов в скачанном нанифесте на имена образов в локальном регистраторе.<br />
<br />
Запустите Ingress-nginx-контролер:<br />
<pre><br />
kubectl apply -f ingress-nginx-deploy.yaml<br />
</pre><br />
<br />
На одном или нескольких kubernet-узлах (эти узла в дальнейшем нужно прописать в файле конфигурации балансировщика <code>haproxy</code>) пробросьте порт <code>nginx-контроллера</code> (<code>31000</code>) из <code>namespace</code> пользователя <code>u7s-admin</code> в сеть <code>kubernetes</code>:<br />
<pre><br />
nsenter_u7s rootlessctl add-ports 0.0.0.0:31000:31000/tcp<br />
</pre><br />
<br />
=== Настройка Ingress-правил ===<br />
<br />
Kubernetes поддерживает манифесты типа Ingress (kind: Ingress) описывающие правила переадресации запросов URL http-запррса на внутренние порты сервисов (kind: Service) kubernetes. Сервисы в свою очередь перенаправляют запросы на реплики Pod'ов, входящих в данный сервис.<br />
<br />
Общий вид Ingress-манифеста:<br />
<pre><br />
apiVersion: networking.k8s.io/v1<br />
kind: Ingress<br />
metadata:<br />
name: <ingress_имя><br />
spec:<br />
ingressClassName: nginx <br />
rules:<br />
- host: <домен_1><br />
http:<br />
paths:<br />
- path: /<br />
pathType: Prefix<br />
backend:<br />
service:<br />
name: <имя_сервиса_1><br />
port:<br />
number: 80<br />
- path: /<тропа_1><br />
pathType: Prefix<br />
backend:<br />
service:<br />
name: <имя_сервиса_2><br />
port:<br />
number: 80<br />
- host: <домен_2><br />
...<br />
</pre><br />
Где:<br />
* <code>host: <домен_1></code>, <code><домен_2></code>, ... - домены WEB-серверов на которых приходит запрос; <br />
* <code>path:/></code>, <code>path:/<тропа_1></code> - тропы (префиксы запросов после домена) <br />
* <code>pathType: Prefix</code> - тип троп: <code>Prefix</code> или <code>Exact</code>;<br />
* <code>service:</code> - имя сервиса на который перенаправляется запрос, если полученный запрос соответсвует правилу;<br />
* <code>port</code> - номер порта на который перенаправляется запрос.<br />
<br />
Если запросу соответствует несколько правил, выбирается правило с наиболее длинным префиксом.<br />
<br />
Подробности смотри в [https://kubernetes.io/docs/concepts/services-networking/ingress/ Kubernetes: Ingress]<br />
<br />
== Настройка haproxy и DNS ==<br />
<br />
Добавьте в файлы конфигурации <code>haproxy</code> <code>/etc/haproxy/haproxy.conf</code> переадресацию запросов на порт <code>80</code> (<code>http</code>) по IP-адресу балансировщика haproxy на IP-адреса <code>kubernet-узлов</code> на которых выбранный порт <code>nginx-контроллера</code> (<code>31000</code>) проброшен из <code>namespace</code> пользователя <code>u7s-admin</code> в сеть <code>kubernetes</code>:<br />
<pre><br />
frontend http<br />
bind *:80<br />
mode tcp<br />
option tcplog<br />
default_backend http<br />
<br />
backend http<br />
mode tcp<br />
balance roundrobin<br />
server <server1> <ip1>:31000 check<br />
server <server2> <ip2>:31000 check<br />
</pre><br />
<br />
Заведите DNS-запись связывающую DNS-имя http-сервиса с IP-адресам <code>haproxy</code>-сервера.<br />
<br />
= Выбор версии kubernetes, имени регистратора и платформы =<br />
<br />
Во время разворачивания узла командами<br />
<pre><br />
kubeadm init <br />
kubeadm join ...<br />
</pre><br />
или при создании архива образов командой <br />
<pre><br />
podsec-k8s-save-oci ...<br />
</pre><br />
есть возможность установкой переменных среды указать версию kubernetes, имя регистратора и платформы:<br />
* U7S_KUBEVERSION - версия kubernetes (v1.26.9, v1.27.7, ...);<br />
* U7S_REGISTRY - имя регистратора (registry.k8s.io, registry.altlinux.org, registry.local);<br />
* U7S_PLATFORM - имя платформы (k8s-c10f1 , k8s-p10, ...)<br />
<br />
== Выбор версии kubernetes ==<br />
<br />
Начиная с версии <code>1.0.9</code> поддерживается возможность выбора устанавливаемой версии <code>kubernetes</code>.<br />
<br />
=== Указании версии основных kubernetes образов ===<br />
<br />
Основные kubernetes-образы загружаются в момент инициализации узла командой <code>kubeadm</code>.<br />
В список основных образов входят:<br />
<pre><br />
kube-apiserver:<версия_kubernetes><br />
kube-controller-manager:<версия_kubernetes><br />
kube-scheduler:<версия_kubernetes><br />
kube-proxy:<версия_kubernetes><br />
pause:<версия__образа_pause><br />
etcd:<версия__образа_etcd><br />
coredns:<версия__образа_coredns><br />
</pre><br />
<br />
Тег образов <code>kube-*</code> совпадает с полным номером версии kubernetes типа v1.<minor>.<patch>. Например <code>v1.26.9</code>.<br />
<br />
Теги образов <code>pause</code>, <code>etcd</code>, <code>coredns</code> "зашиты" как статические переменные в <code>kubeadm</code> и могут отличаться в разных версиях <code>kubernetes</code>.<br />
<br />
Получить список образов для текущей версии <code<kuvernetes</code> можно командой:<br />
<pre><br />
# /usr/bin/kubeadm config images list 2>/dev/null<br />
</pre><br />
Пример вывода:<br />
<pre><br />
registry.k8s.io/kube-apiserver:v1.26.10<br />
registry.k8s.io/kube-controller-manager:v1.26.10<br />
registry.k8s.io/kube-scheduler:v1.26.10<br />
registry.k8s.io/kube-proxy:v1.26.10<br />
registry.k8s.io/pause:3.9<br />
registry.k8s.io/etcd:3.5.9-0<br />
registry.k8s.io/coredns/coredns:v1.9.3<br />
</pre><br />
<br />
Выбор версии определяет переменная среды <code>U7S_KUBEVERSION</code>.<br />
<br />
На <code>1.11.2023</code> при значении <code>U7S_REGISTRY</code> <code>registry.altlinux.org</code> переменная <code>U7S_KUBEVERSION</code> может принимать следующие значения:<br />
<br />
* Mинор версия v1.26:<br />
* <code>v1.26.6</code>; <br />
* <code>v1.26.9</code>;<br />
* <code>v1.26.11</code>; <br />
<br />
* Mинор версия v1.27:<br />
* <code>v1.27.11</code>;<br />
<br />
* Mинор версия v1.28:<br />
* <code>v1.28.7</code>;<br />
<br />
Данный список относится к версиям регистратора <code>registry.altlinux.org</code>. При использовании нативного регистратора <code>registry.k8s.io</code> (пустое значение <code>export U7S_REGISTRY=</code>) можно указывать любую доступную на <code>registry.k8s.io</code> версию.<br />
<br />
Примеры:<br />
<pre><br />
export U7S_REGISTRY=registry.altlinux.org<br />
export U7S_KUBEVERSION=v1.26.11<br />
</pre><br />
<br />
<pre><br />
export U7S_REGISTRY=registry.local<br />
export U7S_KUBEVERSION=v1.27.11<br />
</pre><br />
<br />
<pre><br />
export U7S_REGISTRY=<br />
export U7S_KUBEVERSION=v1.27.5<br />
</pre><br />
<br />
<br />
По умолчанию (при отсутствии значения переменной <code>U7S_KUBEVERSION</code>) принимается максимальная версия образа <br />
<code>kube-apiserver</code>.<br />
<br />
Если номер указанной минорной версии <code>kubernetes</code> отличается от текущего, при вызове команды <code>kubeadm</code><br />
производится удаление текущих <code>rpm-пакетов</code> <code>kubernetes*</code>, <code>cri-o</code> и установка <code>rpm-пакетов</code> указанной версии.<br />
<br />
Возможна ситуация, когда на регистраторе образов отсутствует версия образа, полученная в результате выполнения команды: <br />
<pre><br />
/usr/bin/kubeadm config images list<br />
</pre><br />
Если в переменные среды добавить переменную:<br />
<pre><br />
export U7S_SETAVAILABLEIMAGES=yes<br />
</pre><br />
то в качестве стандартного образа принимается образ с максимальной версией в рамках данной минорной версии <br />
(<code>1.26</code>, <code>1.27</code>, <code>1.28</code>).<br />
Данному образу присваивается тег, полученнфй в результате выполнения команды <code> kubeadm config images</code>.<br />
<br />
=== Указании версии дополнительных kubernetes образов ===<br />
<br />
Кроме основных образов при разворачивании кластера используются дополнительные образы:<br />
* flannel:<U7S_FLANNEL_TAG>;<br />
* flannel-cni-plugin:<U7S_FLANNELCNIPLUGIN_TAG><br />
* cert-manager-webhook:<U7S_CERTMANAGER_TAG>;<br />
* cert-manager-controller:<U7S_CERTMANAGER_TAG>;<br />
* cert-manager-cainjector:<U7S_CERTMANAGER_TAG>.<br />
<br />
> При развертывании <code>kubernetes</code> используется только образ <code>flannel</code>, остальные образы используются только командой <code>podsec-k8s-save-oci</code> при формировании архива пакетов для локального регистратора <code>registry.local</code>.<br />
<br />
По умолчанию принимаюnся следующие версии образов:<br />
* U7S_FLANNEL_TAG - v0.22.3;<br />
* U7S_FLANNELCNIPLUGIN_TAG - v1.2.0;<br />
* U7S_CERTMANAGER_TAG - v1.11.0.<br />
<br />
При необходимости можно изменить версию образа экпортировав перед запускам команды соответствующую переменную. Например:<br />
<pre><br />
export U7S_FLANNEL_TAG=v0.19.2<br />
</pre><br />
<br />
== Выбор исходного регистратора kubernetes-образов ==<br />
<br />
Во время инициализации <code>master-узла</code> кластера (<code>kubeadm init</code>) или во время подключения узла к кластеру (<code>kubeadm join</code>) команда <code>kubeadm</code> может загружать образы с различных регистраторов образов и с различными префиксами.<br />
<br />
Выбор регистратора и префикса образов определяет переменная среды <code>U7S_REGISTRY</code>. <br />
Если переменная не задана регистратор префикс определяется автоматически на основе конфигурационных файлов <code>/etc/os-release</code> и <code>/etc/hosts</code>. <br />
<br />
Переменная среды <code>U7S_REGISTRY</code> может принимать следующие основные значения:<br />
* пустое значение;<br />
* <code>registry.altlinux.org</code>;<br />
* <code>registry.local</code>;<br />
* ...<br />
<br />
=== Нативные kubernetes-образы ===<br />
<br />
<pre><br />
export U7S_REGISTRY=<br />
</pre><br />
Если переменная <code>U7S_REGISTRY</code> установлена в пустое значение образы загружаются со стандартного регистратора образов <code>kubernetes</code>.<br />
<br />
=== Образы altlinux ===<br />
<br />
==== Регистратор registry.altlinux.org ====<br />
<br />
<pre><br />
export U7S_REGISTRY=registry.altlinux.org<br />
</pre><br />
С регистратора <code>altlinux</code> устанавливаются образы при наличии доступа в Интернет.<br />
<br />
==== Локальный регистратор ====<br />
<br />
<pre><br />
export U7S_REGISTRY=registry.local<br />
</pre><br />
<br />
Локальный регистратор используется в сертифицированных дистрибутивах, которые содержат kubernetes-образы на установочном диске.<br />
<br />
Локальный регистратор образов <code>registry.local</code> может обеспечивать:<br />
* разворачивание кластера без доступа в Интернет;<br />
* ускоренное разворачивание как кластера, так и проектов, разворачиваемых в его рамках, так как образы необходимые для запуска <code>Pod</code>'ов загружаются по локальной сети; <br />
* высокий уровень защищенности системы путем установки политик разрешающих загрузку только подписанных образов и только с локального регистратора <code>registry.local</code>.<br />
<br />
Пакет <code>podsec</code> обеспечивает:<br />
* Установку на рабочих местах клиентов и узлах <code>kubernetes</code> политик доступа к образом для различных категория пользователей (скрипт <code>podsec-create-policy</code>).<br />
* Разворачивание на одном узлов локального регистратора образов и сервера подписей образов (скрипт <code>podsec-create-services</code>). <br />
* Загрузку с регистратора <code>registry.altlinux.org</code> образов необходимых для разворачивания <code>kubernetes</code> и формирования максимально сжатого (<200Mb) архива. (скрипты <code>podsec-k8s-save-oci</code>, <code>podsec-save-oci</code>)<br />
* разворачивание образов из архива, их подпись размещение на локальном регистраторе (скрипт <code>podsec-load-sign-oci</code>). <br />
<br />
В зависимости от значения переменных <code>U7S_REGISTRY</code>, <code>U7S_PLATFORM</code>, <code>U7S_KUBEVERSION</code> скрипт <code>podsec-k8s-save-oci</code> формирует архив образов различных версий kubernetes:<br />
* <code>registry.local/k8s-c10f1</code> - архив образов для сертифицированного дистрибутива <code>c10</code> на основе набора образов с регистратора <code>registry.altlinux.org</code> с платформой <code>k8s-c10f1</code>;<br />
* <code>registry.local/k8s-p10</code> - архив образов для несертифицированного дистрибутива <code>p10</code> на основе набора образов с регистратора <code>registry.altlinux.org</code> с платформой <code>k8s-p10</code>;<br />
<br />
Локальный регистратор <code>registry.local</code> может также хранить подписанные образы и запускаемых в рамках кластера проектов. Необходимо только, чтобы каждый образ в рамках локального регистратор <code>registry.local</code> имел префикс. Образы типа <code>registry.local/<имя_образа></code> не допускаются, так как для них трудно определить "подписанта" образа.<br />
<!--<br />
* <code>export U7S_REGISTRY=registry.local/k8s-c10f1</code> - образы для сертифицированного дистрибутива <code>c10</code>;<br />
* <code>export U7S_REGISTRY=registry.local/k8s-p10</code> - образы для несертифицированного дистрибутива <code>p10</code>.<br />
--><br />
<br />
===== podsec-create-policy - настройка политики доступа к образам различным категориям пользователей =====<br />
<br />
'''Формат''':<br />
<pre>podsec-create-policy <ip-адрес_регистратора_и_сервера_подписей></pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт <code>podsec-create-policy</code> формирует в файлах <code>/etc/containers/policy.json</code>, <br />
<code>/etc/containers/registries.d/default.yaml</code> максимально защищенную политику доступа к образам - по умолчанию допускается доступ только к подписанным образам локального регистратора <code>registry.local</code>.<br />
Данная политика распространяется как на пользователей имеющих права суперпользователя, так и на пользователей группы <code>podsec</code>, создаваемые podsec-скриптом <code>podsec-create-podmanusers</code>. <br />
<br />
Пользователи группы <code>podsec-dev</code>, создаваемые podsec-скриптом <code>podsec-create-imagemakeruser</code> имеют неограниченные права на доступ, формирования образов, их подпись и помещение на локальный регистратор <code>registry.local</code>.<br />
<br />
В разворачиваниях kubernetes не требующих таких жестких ограничений в политике доступа и работы с образами политики могут быть смягчены путем модифицирования cистемных файлов политик <code>/etc/containers/policy.json</code>, <code>/etc/containers/registries.d/default.yaml</code> или файлов установки политик пользователей <code>~/.config/containers/policy.json</code>, <code>~/.config/containers/registries.d/default.yaml</code>.<br />
<br />
===== podsec-create-services - разворачивание локального регистратора образов и сервера подписей образов =====<br />
<br />
Скрипт <code>podsec-create-services</code> обеспечивает разворачивание локального регистратора образов и сервера подписей образов.<br />
<br />
===== Поддержка электронной подписи образов =====<br />
<br />
Для <code>kubernetes-образов</code>, хранящихся в архиве образов распаковку образов, их подпись и размещение на локальном регистраторе <code>registry.local</code> обеспечивает скрипт <code>podsec-load-sign-oci</code> запускаемый пользователем группы <code>podsec-dev</code>.<br />
<br />
Для других образов пользователь группы <code>podsec-dev</code> должен создать образ в домене локального регистратора <code>registry.local/</prefix>/</code> и поместить его в регистратор командой:<br />
<pre>podman push --tls-verify=false --sign-by="<email-подписанта" <образ></pre><br />
<br />
Образ в домене <code>registry.local/</prefix>/</code> может быть получен:<br />
* присваивании алиаса стороннему образу:<br />
<pre>podman tag <сторонний_образ> registry.local/</prefix>/<локальный_образ></pre><br />
* сборки образов через <code>Dockerfile</code>.<br />
<pre>podman build -t registry.local/</prefix>/<локальный_образ> ...</pre><br />
<br />
== Указание платформы ==<br />
<br />
Кроме имени регистратора kubernetes-образы altlinux содержит в имени (например registry.altlinux.org/k8s-p10/kube-apiserver) название платформы:<br />
* k8s-p10 - образы для дистрибутива p10;<br />
* k8s-c10f1 - образы сертифицированного дистрибутива c10;<br />
* test_k8s - тестовые образы;<br />
* ...<br />
<br />
Платформу устанавливаемых образов можно указать в переменной <code>U7S_PLATFORM</code>. Например:<br />
<pre><br />
export U7S_PLATFORM=test_k8s<br />
</pre><br />
<br />
== Автоматический выбор регистратора образов и платформы ==<br />
<br />
Если переменная <code>U7S_REGISTRY</code> не установлена, ее значения вычисляется автоматически по следующему алгоритму:<br />
<br />
* Если файл <code>/etc/hosts</code> содержит описание хоста <code>registry.local</code> префикс переменной <code>U7S_REGISTRY</code> принимает значение <code>registry.local/</code>, иначе <code>registry.altlinux.org/</code>.<br />
* Если переменная <code>CPE_NAME</code> файла <code>/etc/os-release</code> содержит значение <code>spserver</code> суффикс переменной <code>U7S_PLATFORM</code> принимает значение <code>k8s-c10f1</code>, иначе <code>k8s-p10</code>.<br />
<br />
= podsec-k8s-rbac - Поддержка управление доступом на основе ролей (RBAC) =<br />
<br />
В пакет <code>podsec-k8s-rbac</code> входит набор скриптов для работы с <code>RBAC</code> - <code>Role Based Access Control</code>:<br />
* <code>podsec-k8s-rbac-create-user</code> - создание <code>RBAC-пользователя</code>;<br />
* <code>podsec-k8s-rbac-create-kubeconfig</code> - создание ключей, сертификатов и файла конфигурации <code>RBAC-пользователя</code>;<br />
* <code>podsec-k8s-rbac-create-remoteplace</code> - создание удаленного рабочего места;<br />
* <code>podsec-k8s-rbac-bindrole</code> - привязывание пользователя к кластерной или обычной роли;<br />
* <code>podsec-k8s-rbac-get-userroles</code> - получить список кластерные и обычных ролей пользователя;<br />
* <code>podsec-k8s-rbac-unbindrole</code> - отвязывание пользователя от кластерной или обычной роли.<br />
<br />
== podsec-k8s-rbac-create-user - создание RBAC-пользователя ==<br />
<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-user имя_пользователя</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт:<br />
* создает RBAC пользователя<br />
* создает в домашнем директории каталог .kube<br />
* устанавливаются соответствующие права доступа к каталогам.<br />
<br />
== podsec-k8s-rbac-create-kubeconfig - создание ключей, сертификатов и файла конфигурации RBAC-пользователя ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-kubeconfig имя_пользователя[@<имя_удаленного_пользователя>] [группа ...]</pre><br />
<br />
'''Описание''':<br />
Скрипт должен вызываться администратором безопасности средства контейнеризации.<br />
<br />
Для <code>rootless</code> решения имя удаленного пользователя принимается <code>u7s-admin</code>.<br />
<br />
Для <code>rootfull</code> решения необходимо после символа <code>@</code> указать <code>имя удаленного пользователя</code>.<br />
<br />
Скрипт в каталоге <code>~имя_пользователя/.kube производит</code>:<br />
* Создании личного (private) ключа пользователя (файл <code>имя_пользователя.key</code>).<br />
* Создание запроса на подпись сертификата (CSR) (файл <code>имя_пользователя.key</code>).<br />
* Запись <code>запроса на подпись сертификата CSR </code>в кластер.<br />
* Подтверждение <code>запроса на подпись сертификата (CSR)</code>.<br />
* Создание <code>сертификата</code> (файл <code>имя_пользователя.crt</code>).<br />
* Проверку корректности сертификата<br />
* Формирование файла конфигурации пользователя (файл <code>config</code>)<br />
* Добавление контекста созданного пользователя<br />
<br />
== podsec-k8s-rbac-create-remoteplace - создание удаленного рабочего места ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-create-remoteplace ip-адрес</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит настройку удаленного рабочего места пользователя путем копирования его конфигурационного файла.<br />
<br />
== podsec-k8s-rbac-bindrole - привязывание пользователя к кластерной или обычной роли ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-bindrole имя_пользователя role|role=clusterrole|clusterrole роль имя_связки_роли [namespace]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит привязку пользователя к обычной или кластерной роли используя имя_связки_роли.<br />
<br />
'''Параметры''':<br />
<br />
* имя_пользователя должно быть создано командой podsec-k8s-rbac-create-user и сконфигурировано на доступ к кластеру командой podsec-k8s-rbac-create-kubeconfig;<br />
* тип роли может принимать следующие значения:<br />
* role - пользователь привязывется к обычной роли с именем <роль> (параметр namespace в этом случае обязателен);<br />
* role=clusterrole - пользователь привязывется к обычной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае обязателен);<br />
* clusterrole - пользователь привязывется к кластерной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае должен отсутствовать).<br />
* роль - имя обычной или кластерной роли в зависимости от предыдущего параметра;<br />
* имя_связки_роли - имя объекта класса rolebindings или clusterrolebindings в зависимости от параметра тип роли. В рамках этого объекта к кластерной или обычной роли могут быть привязаны несколько пользователей.<br />
* namespace - имя namespace для обычной роли.<br />
<br />
== podsec-k8s-rbac-get-userroles - получить список кластерные и обычных ролей пользователя ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-get-userroles имя_пользователя [showRules]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт формирует список кластерные и обычных ролей которые связаны с пользователем. При указании флага <code>showRules</code>, для каждой роли указывается список правил ("<code>rules:[...]</code>"), которые принадлежат каждой роли пользователя.<br />
<br />
Результат возвращается в виде <code>json-строки</code> формата:<br />
<pre><br />
{<br />
"": {<br />
"clusterRoles": [...],<br />
"roles": {<br />
"allNamespaces": [...],<br />
"namespaces": [<br />
{<br />
"": [...],<br />
...<br />
}<br />
}<br />
}<br />
}<br />
</pre><br />
Где <code>[...]</code> - массив объектов типа:<br />
<pre><br />
{<br />
"bindRoleName": "",<br />
"bindedRoleType": "ClusterRole|Role",<br />
"bindedRoleName": "",<br />
"unbindCmd": "podsec-k8s-rbac-unbindrole ..."<br />
}<br />
</pre><br />
<br />
== podsec-k8s-rbac-unbindrole - отвязывание пользователя от кластерной или обычной роли ==<br />
'''Формат''':<br />
<pre>podsec-k8s-rbac-unbindrole имя_пользователя role|clusterrole роль имя_связки_роли [namespace]</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт производит отвязку роли от кластерной или обычной роли, созданной командой <code>podsec-k8s-rbac-bindrole</code>. Полный текст команды можно получить в выводе команды <code>podsec-k8s-rbac-get-userroles</code> в поле <code>unbindCmd</code>. Если в указанном имя_связки_роли объекте класса <code>rolebindings</code> или <code>clusterrolebindings</code> еще остаются пользователи - объект модифицируется. Если список становится пуст - объект удаляется.<br />
<br />
'''Параметры''':<br />
<br />
* <code>имя_пользователя</code> должно быть создано командой <code>podsec-k8s-rbac-create-user</code> и сконфигурировано на доступ к кластеру командой <code>podsec-k8s-rbac-create-kubeconfig</code>;<br />
* тип роли может принимать следующие значения:<br />
* <code>role</code> - пользователь привязывается к обычной роли с именем <роль> (параметр <code>namespace</code> в этом случае обязателен);<br />
* <code>clusterrole</code> - пользователь привязывается к кластерной роли используя кластерную роль с именем <роль> (параметр <code>namespace</code> в этом случае должен отсутствовать).<br />
* <code>роль</code> - имя обычной или кластерной роли в зависимости от предыдущего параметра;<br />
* <code>имя_связки_роли</code> - имя объекта класса <code>rolebindings</code> или <code>clusterrolebindings</code> в зависимости от параметра тип роли. В рамках этого объекта к кластерной или обычной роли могут быть привязаны несколько пользователей.<br />
* <code>namespace</code> - имя <code>namespace</code> для обычной роли.<br />
<br />
= podsec-inotify - Мониторинг безопасности системы =<br />
<br />
В пакет podsec-inotify входит набор скриптов для мониторинга безопасности системы: <br />
* podsec-inotify-check-policy - проверка настроек политики контейнеризации на узле;<br />
* podsec-inotify-check-containers - проверка наличия изменений файлов в директориях rootless контейнерах;<br />
* podsec-inotify-check-images - проверка образов на предмет их соответствия настройки политикам контейнеризации на узле;<br />
* podsec-inotify-check-kubeapi - мониторинг аудита API-интерфейса kube-apiserver control-plane узла;<br />
* podsec-inotify-check-vuln - мониторинг docker-образов узла сканером безопасности trivy.<br />
<br />
== podsec-inotify-check-policy - проверка настроек политики контейнеризации на узле ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-policy [-v[vv]] [-a интервал] [-f интервал] -c интервал -h интервал [-m интервал] х-w интервалъ [-l интервал] [-d интервал]</pre><br />
<br />
'''Описание''':<br />
Плугин проверяет настройки политики контейнеризации на узле.<br />
<br />
Проверка идет по следующим параметрам:<br />
<br />
* файл <code>policy.json</code> установки транспортов и политик доступа к регистраторам:<br />
{| class="wikitable"<br />
|+<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| имеющих <code>defaultPolicy != reject</code>, но не входящих в группу <code>podman_dev</code> || 102<br />
|-<br />
| не имеющих не имеющих <code>registry.local</code> в списке регистраторов для которых проверяется наличие электронной подписи образов || 103<br />
|-<br />
| имеющих в политике регистраторы для которых не проверяется наличие электронной подписи образов || 104<br />
|-<br />
| имеющих в списке поддерживаемых транспорты отличные от <code>docker</code> (транспорт получения образов с регистратора) || 105<br />
|}<br />
<br />
<br />
* файлы привязки регистраторов к серверам хранящим электронные подписи (файл привязки о умолчанию <code>default.yaml</code> и файлы привязки регистраторов <code>*.yaml</code> каталога <code>registries.d</code>). Наличие (число) пользователей:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| не использующих хранилище подписей <code>http://sigstore.local:81/sigstore/</code> как хранилище подписей по умолчанию || 106<br />
|}<br />
<br />
* контроль групп пользователей<br />
# наличие пользователей имеющих образы, но не входящих в группу <code>podman</code>:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| наличие пользователей имеющих образы, но не входящих в группу <code>podman</code> || 101<br />
|}<br />
# наличие пользователей группы <code>podman</code> (за исключением входящих в группу <code>podman_dev</code>):<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| входящих в группу <code>wheel</code> || 101<br />
|-<br />
| имеющих каталог <code>.config/containers/</code> открытым на запись и изменения || 90 * <code>доля_нарушителей</code><br />
|-<br />
| не имеющих файла конфигурации <code>.config/containers/storage.conf</code> || 90 * <code>доля_нарушителей</code><br />
|}<br />
<code>доля_нарушителей</code> считается как: <code>число_нарушителей / число_пользователей_группы_podman</code><br />
<br />
Все веса метрик суммируются и формируется итоговая метрика.<br />
<br />
== podsec-inotify-check-containers - проверка наличия изменений файлов в директориях rootless контейнерах ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-containers</pre><br />
<br />
'''Описание''':<br />
<br />
Скрипт:<br />
* создаёт список директорий <code>rootless</code> контейнеров, существующих в системе,<br />
* запускает проверку на добавление,удаление, и изменение файлов в директориях контейнеров,<br />
* отсылает уведомление об изменении в системный лог.<br />
<br />
== podsec-inotify-check-images - проверка образов на предмет их соответствия настройки политикам контейнеризации на узле ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-images [-v[vv]] [-a интервал] [-f интервал] -c интервал -h интервал [-m интервал] х-w интервалъ [-l интервал] [-d интервал]</pre><br />
<br />
'''Описание''':<br />
<br />
Плугин проверяет образы на предмет их соответствия настройки политикам контейнеризации на узле. Проверка идет по следующим параметрам:<br />
{| class="wikitable"<br />
|-<br />
! Параметр контроля пользователей !! Вес метрики<br />
|-<br />
| наличие в политике пользователя регистраторов не поддерживающие электронную подпись || 101<br />
|-<br />
| наличие в кэше образов неподписанных образов || 101<br />
|-<br />
| наличие в кэше образов вне поддерживаемых политик || 101<br />
|}<br />
Все веса метрик суммируются и формируется итоговая метрика.<br />
<br />
== podsec-inotify-check-kubeapi - мониторинг аудита API-интерфейса kube-apiserver control-plane узла ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-kubeapi [-d]</pre><br />
<br />
'''Описание''':<br />
Скрипт производит мониторинг файла <code>/etc/kubernetes/audit/audit.log</code> аудита API-интерфейса <code>kube-apiserver</code>.<br />
<br />
Политика аудита располагается в файле <code>/etc/kubernetes/audit/policy.yaml</code>:<br />
<pre><br />
apiVersion: audit.k8s.io/v1<br />
kind: Policy<br />
omitManagedFields: true<br />
rules:<br />
# do not log requests to the following <br />
- level: None<br />
nonResourceURLs:<br />
- "/healthz*"<br />
- "/logs"<br />
- "/metrics"<br />
- "/swagger*"<br />
- "/version"<br />
- "/readyz"<br />
- "/livez"<br />
<br />
- level: None<br />
users:<br />
- system:kube-scheduler<br />
- system:kube-proxy<br />
- system:apiserver<br />
- system:kube-controller-manager<br />
- system:serviceaccount:gatekeeper-system:gatekeeper-admin<br />
<br />
- level: None<br />
userGroups:<br />
- system:nodes<br />
- system:serviceaccounts<br />
- system:masters<br />
<br />
# limit level to Metadata so token is not included in the spec/status<br />
- level: Metadata<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: authentication.k8s.io<br />
resources:<br />
- tokenreviews<br />
<br />
# extended audit of auth delegation<br />
- level: RequestResponse<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: authorization.k8s.io<br />
resources:<br />
- subjectaccessreviews<br />
<br />
# log changes to pods at RequestResponse level<br />
- level: RequestResponse<br />
omitStages:<br />
- RequestReceived<br />
resources:<br />
- group: "" # core API group; add third-party API services and your API services if needed<br />
resources: ["pods"]<br />
verbs: ["create", "patch", "update", "delete"]<br />
<br />
# log everything else at Metadata level<br />
- level: Metadata<br />
omitStages:<br />
- RequestReceived<br />
</pre> <br />
<br />
Текущие настройки производят логирование всех обращений "несистемных" пользователей (в том числе анонимных) к ресурсам <code>kubernetes</code>.<br />
<br />
Скрипт производит выборку всех обращений, в ответ на которые был сформирован код более <code>400</code> - запрет доступа. <br />
Все эти факты записываются в системный журнал и накапливаются в файле логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code>, который периодически передается через посту системному адмиристратору.<br />
<br />
'''Параметры''':<br />
<br />
* <code>-d</code> - скирпт запускается в режиме демона, производящего онлайн мониторинг файла <code>/etc/kubernetes/audit/audit.log</code> и записывающего факты запросов с запретом доступа в системный журнал и файл логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code>.<br />
<br />
* при запуске без параметров скрипт посылает файл логов <code>/var/lib/podsec/u7s/log/kubeapi/forbidden.log</code> почтой системному администратору (пользователь <code>root</code>) и обнуляет файл логов.<br />
<br />
В состав пакета кроме этого скрипта входят:<br />
<br />
* файл описания сервиса </code>/lib/systemd/system/podsec-inotify-check-kubeapi.service</code>. Для его запуска екобходимо выполнить команды:<br />
<pre> <br />
# systemctl enable podsec-inotify-check-kubeapi.service<br />
# systemctl start podsec-inotify-check-kubeapi.service<br />
</pre><br />
<br />
* файл для </code>cron</code> </code>/etc/podsec/crontabs/podsec-inotify-check-kubeapi</code>. Файл содержит единственную строку с описанием режима запуска скрипта </code>podsec-inotify-check-kubeapi</code> для передачи почты системному администратору. <br />
Скрипт запускается один раз в 10 минут.<br />
Во время установки пакета строка файла (в случае ее отсутствия) дописыватся в </code>crontab</code>-файл </code>/var/spool/cron/root</code> пользователя </code>root</code>. <br />
Если необходимо изменить режим запуска скрипта или выключить его это можно сделать командой редактирования </code>crontab</code>-файла:<br />
<pre><br />
# crontab -e<br />
</pre><br />
<br />
== podsec-inotify-check-vuln - мониторинг docker-образов узла сканером безопасности trivy ==<br />
<br />
'''Формат''':<br />
<pre>podsec-inotify-check-vuln</pre><br />
<br />
'''Описание''':<br />
<br />
<br />
<br />
Для корректной работы скрипта необходимо запустить сервис <code>podsec-inotify-server-trivy</code>:<br />
<pre><br />
systemctl enable --now podsec-inotify-server-trivy<br />
</pre><br />
<br />
Скрипт производит мониторинг <code>docker-образов</code> узла сканером безопасности <code>trivy</code>:<br />
<br />
* Если скрипт запускается от имени пользователя <code>root</code> скрипт:<br />
# проверяет сканером <code>trivy</code> <code>rootfull</code> образы;<br />
# для всех пользователей каталога <code>/home/</code> проверяется наличие <code>rootless</code>-образов. При их наличии проверяет сканером <code>trivy</code> эти образы.<br />
<br />
* Если скрипт запускается от имени обычного пользователя проверяется наличие <code>rootless</code>-образов. При их наличии проверяет сканером <code>trivy</code> эти образы.<br />
<br />
Результат анализа посылается в системный лог.<br />
Если при анализе образа число обнаруженных угроз уровня <code>HIGH</code> больше 0, результат посылается почтой системному администратору (<code>root</code>).<br />
<br />
'''Параметры''':<br />
<br />
Отсутствуют.<br />
<br />
В состав пакета кроме этого скрипта входит файл для <code>cron</code> <code>/etc/podsec/crontabs/podsec-inotify-check-vuln</code>. Файл содержит единственную строку с описанием режима запуска скрипта <code>podsec-inotify-check-vuln</code>.<br />
Во время установки пакета строка файла (в случае ее отсутствия) дописыватся в <code>crontab</code>-файл <code>/var/spool/cron/root</code> пользователя <code>root</code>.<br />
<br />
Если необходимо изменить режим запуска скрипта или выключить его это можно сделать командой редактирования <code>crontab</code>-файла:<br />
<pre><br />
# crontab -e<br />
</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78831Podman-compose-to-kube2024-03-05T16:42:41Z<p>Kaf: /* Генерация манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -v pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d -v pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
'''Файлы описания Deployment-решения redis.yml, web.yml'''<br />
<br />
Файлы описания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно перед запуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как при Deployment-разворачивания контейнеры разворачиваются в отдельных POD'ах<br />
и оба имеют порты, то для каждого из них генерируются отдельный файл описания сервисов <code>Service/redis.yml</code>, <code>Service/web.yml</code>.<br />
<br />
Файл <code>redis.yml</code> описания сервиса <code>redis</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: redis<br />
name: redis<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 30921<br />
port: 6379<br />
targetPort: 6379<br />
selector:<br />
app: redis<br />
type: NodePort<br />
</pre><br />
Если к сервису <code>Service/web.yml</code> не необходимости обращении извне елемент <code>spec.ports[0].nodePort</code> можно удалить.<br />
<br />
Файл <code>web.yml</code> описания сервиса <code>web</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: web<br />
name: web<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '8080'<br />
nodePort: 31434<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: web<br />
type: NodePort<br />
</pre><br />
<br />
'''Подкаталоги <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code>'''<br />
Структура и содержание подкаталогов <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code> разворачивания <code>Deployment</code> совпадает с разворачиванием <code>Pod</code>, описанное выше.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>Deployment-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Deployment/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/redis created<br />
service/web created<br />
deployment.apps/redis created<br />
deployment.apps/web created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Deployment/</code>.<br />
<br />
При необходимости Вы можете реплицировать (например в количестве 3) Deployment web командой:<br />
<pre><br />
kubectl scale --replicas=3 deployment web<br />
</pre><br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/redis-7595cd897c-894dd 1/1 Running 0 3m46s 10.88.0.103 host-8 <none> <none><br />
pod/web-5778c5c-b8gcw 1/1 Running 0 3m46s 10.88.0.102 host-8 <none> <none><br />
pod/web-5778c5c-h7bjh 1/1 Running 0 7s 10.88.0.104 host-8 <none> <none><br />
pod/web-5778c5c-nqxhs 1/1 Running 0 7s 10.88.0.105 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/redis NodePort 10.110.219.99 <none> 6379:30921/TCP 3m46s app=redis<br />
service/web NodePort 10.103.86.45 <none> 8080:31434/TCP 3m46s app=web<br />
<br />
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR<br />
deployment.apps/redis 1/1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis<br />
deployment.apps/web 3/3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web<br />
<br />
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR<br />
replicaset.apps/redis-7595cd897c 1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis,pod-template-hash=7595cd897c<br />
replicaset.apps/web-5778c5c 3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web,pod-template-hash=5778c5c<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
Для проверки работы POD'а запустите (если не сделали это ранее) контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>web.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://web.default:8080<br />
</pre><br />
<pre><br />
counter=3<br />
</pre><br />
<br />
<br />
''Обратите внимание, что в отличие от разворачивания Pod (домен <code>counter.default</code>) идет обращение к домену <code>web.default</code>.''<br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:31434<br />
</pre> <br />
<pre><br />
counter=4<br />
</pre><br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Deployment/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "redis" deleted<br />
service "web" deleted<br />
deployment.apps "redis" deleted<br />
deployment.apps "web" deleted<br />
</pre><br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
При генерации для rootless-kubernetes укажите при вызове команды <code>podman-compose-to-kube</code> имя пользователя флагом <code>-u</code> (<code>--user</code>) имя пользователя под которым работает <code>kubernetes</code> (и имя группы флагом <code>-п</code> (<code>--group</code>), если имя группа отличается от имени пользователя).<br />
Например, если <code>kubernetes</code> работает под пользователем <code>u7s-admin</code> команда генерации <code>Deployment-разворачивания</code> выглядит так:<br />
<pre><br />
podman-compose-to-kube -u u7s-admin -t d pod_counter docker-compose.yaml<br />
</pre><br />
<br />
При указании флага <code>-u</code> для создаваемых в локальной файловой системе томов в качестве владельцев устанавливаются указанные в параметрах пользователь и группа.<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> в рамках пользователя `u7s-admin` хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>.<br />
<br />
Если образ был собран в рамках пользователя `u7s-admin`: <br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
<br />
Если образ был собран в рамках пользователя `root`: <br />
<pre><br />
# skopeo copy containers-storage:[/var/lib/containers/storage/]localhost/hello-py-aioweb containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====<br />
<br />
В <code>rootless-режиме</code> все создаваемые Nodeport-порты остаются в namespace пользователя от имени которого запущен <code>kubernetes</code>.<br />
Для проброса портов наружу необходимо зайти в <code>namepspace</code> пользователя командой:<br />
<pre><br />
machinectl shell u7s-admin@ /usr/libexec/podsec/u7s/bin/nsenter_u7s<br />
</pre><br />
<pre><br />
[INFO] Entering RootlessKit namespaces: OK<br />
[root@host ~]# <br />
</pre><br />
и сделать проброс портов:<br />
<pre><br />
# rootlessctl \<br />
--socket /run/user/989/usernetes/rootlesskit/api.sock <br />
add-ports "0.0.0.0:30748:30748/tcp"<br />
</pre><br />
<pre><br />
10<br />
</pre><br />
Где:<br />
* <code>989</code> - идентификатор (uid) пользователя <code>u7s-admin</code>;<br />
* <code>30748</code> - номер пробрасываемого порта.</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78830Podman-compose-to-kube2024-03-05T16:42:12Z<p>Kaf: /* Генерация манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d -v pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
'''Файлы описания Deployment-решения redis.yml, web.yml'''<br />
<br />
Файлы описания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно перед запуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как при Deployment-разворачивания контейнеры разворачиваются в отдельных POD'ах<br />
и оба имеют порты, то для каждого из них генерируются отдельный файл описания сервисов <code>Service/redis.yml</code>, <code>Service/web.yml</code>.<br />
<br />
Файл <code>redis.yml</code> описания сервиса <code>redis</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: redis<br />
name: redis<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 30921<br />
port: 6379<br />
targetPort: 6379<br />
selector:<br />
app: redis<br />
type: NodePort<br />
</pre><br />
Если к сервису <code>Service/web.yml</code> не необходимости обращении извне елемент <code>spec.ports[0].nodePort</code> можно удалить.<br />
<br />
Файл <code>web.yml</code> описания сервиса <code>web</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: web<br />
name: web<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '8080'<br />
nodePort: 31434<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: web<br />
type: NodePort<br />
</pre><br />
<br />
'''Подкаталоги <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code>'''<br />
Структура и содержание подкаталогов <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code> разворачивания <code>Deployment</code> совпадает с разворачиванием <code>Pod</code>, описанное выше.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>Deployment-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Deployment/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/redis created<br />
service/web created<br />
deployment.apps/redis created<br />
deployment.apps/web created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Deployment/</code>.<br />
<br />
При необходимости Вы можете реплицировать (например в количестве 3) Deployment web командой:<br />
<pre><br />
kubectl scale --replicas=3 deployment web<br />
</pre><br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/redis-7595cd897c-894dd 1/1 Running 0 3m46s 10.88.0.103 host-8 <none> <none><br />
pod/web-5778c5c-b8gcw 1/1 Running 0 3m46s 10.88.0.102 host-8 <none> <none><br />
pod/web-5778c5c-h7bjh 1/1 Running 0 7s 10.88.0.104 host-8 <none> <none><br />
pod/web-5778c5c-nqxhs 1/1 Running 0 7s 10.88.0.105 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/redis NodePort 10.110.219.99 <none> 6379:30921/TCP 3m46s app=redis<br />
service/web NodePort 10.103.86.45 <none> 8080:31434/TCP 3m46s app=web<br />
<br />
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR<br />
deployment.apps/redis 1/1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis<br />
deployment.apps/web 3/3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web<br />
<br />
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR<br />
replicaset.apps/redis-7595cd897c 1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis,pod-template-hash=7595cd897c<br />
replicaset.apps/web-5778c5c 3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web,pod-template-hash=5778c5c<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
Для проверки работы POD'а запустите (если не сделали это ранее) контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>web.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://web.default:8080<br />
</pre><br />
<pre><br />
counter=3<br />
</pre><br />
<br />
<br />
''Обратите внимание, что в отличие от разворачивания Pod (домен <code>counter.default</code>) идет обращение к домену <code>web.default</code>.''<br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:31434<br />
</pre> <br />
<pre><br />
counter=4<br />
</pre><br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Deployment/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "redis" deleted<br />
service "web" deleted<br />
deployment.apps "redis" deleted<br />
deployment.apps "web" deleted<br />
</pre><br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
При генерации для rootless-kubernetes укажите при вызове команды <code>podman-compose-to-kube</code> имя пользователя флагом <code>-u</code> (<code>--user</code>) имя пользователя под которым работает <code>kubernetes</code> (и имя группы флагом <code>-п</code> (<code>--group</code>), если имя группа отличается от имени пользователя).<br />
Например, если <code>kubernetes</code> работает под пользователем <code>u7s-admin</code> команда генерации <code>Deployment-разворачивания</code> выглядит так:<br />
<pre><br />
podman-compose-to-kube -u u7s-admin -t d pod_counter docker-compose.yaml<br />
</pre><br />
<br />
При указании флага <code>-u</code> для создаваемых в локальной файловой системе томов в качестве владельцев устанавливаются указанные в параметрах пользователь и группа.<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> в рамках пользователя `u7s-admin` хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>.<br />
<br />
Если образ был собран в рамках пользователя `u7s-admin`: <br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
<br />
Если образ был собран в рамках пользователя `root`: <br />
<pre><br />
# skopeo copy containers-storage:[/var/lib/containers/storage/]localhost/hello-py-aioweb containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====<br />
<br />
В <code>rootless-режиме</code> все создаваемые Nodeport-порты остаются в namespace пользователя от имени которого запущен <code>kubernetes</code>.<br />
Для проброса портов наружу необходимо зайти в <code>namepspace</code> пользователя командой:<br />
<pre><br />
machinectl shell u7s-admin@ /usr/libexec/podsec/u7s/bin/nsenter_u7s<br />
</pre><br />
<pre><br />
[INFO] Entering RootlessKit namespaces: OK<br />
[root@host ~]# <br />
</pre><br />
и сделать проброс портов:<br />
<pre><br />
# rootlessctl \<br />
--socket /run/user/989/usernetes/rootlesskit/api.sock <br />
add-ports "0.0.0.0:30748:30748/tcp"<br />
</pre><br />
<pre><br />
10<br />
</pre><br />
Где:<br />
* <code>989</code> - идентификатор (uid) пользователя <code>u7s-admin</code>;<br />
* <code>30748</code> - номер пробрасываемого порта.</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78824Podman-compose-to-kube2024-03-03T14:21:12Z<p>Kaf: /* Копирование локальных образов в rootless окружении */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
'''Файлы описания Deployment-решения redis.yml, web.yml'''<br />
<br />
Файлы описания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно перед запуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как при Deployment-разворачивания контейнеры разворачиваются в отдельных POD'ах<br />
и оба имеют порты, то для каждого из них генерируются отдельный файл описания сервисов <code>Service/redis.yml</code>, <code>Service/web.yml</code>.<br />
<br />
Файл <code>redis.yml</code> описания сервиса <code>redis</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: redis<br />
name: redis<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 30921<br />
port: 6379<br />
targetPort: 6379<br />
selector:<br />
app: redis<br />
type: NodePort<br />
</pre><br />
Если к сервису <code>Service/web.yml</code> не необходимости обращении извне елемент <code>spec.ports[0].nodePort</code> можно удалить.<br />
<br />
Файл <code>web.yml</code> описания сервиса <code>web</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: web<br />
name: web<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '8080'<br />
nodePort: 31434<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: web<br />
type: NodePort<br />
</pre><br />
<br />
'''Подкаталоги <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code>'''<br />
Структура и содержание подкаталогов <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code> разворачивания <code>Deployment</code> совпадает с разворачиванием <code>Pod</code>, описанное выше.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>Deployment-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Deployment/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/redis created<br />
service/web created<br />
deployment.apps/redis created<br />
deployment.apps/web created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Deployment/</code>.<br />
<br />
При необходимости Вы можете реплицировать (например в количестве 3) Deployment web командой:<br />
<pre><br />
kubectl scale --replicas=3 deployment web<br />
</pre><br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/redis-7595cd897c-894dd 1/1 Running 0 3m46s 10.88.0.103 host-8 <none> <none><br />
pod/web-5778c5c-b8gcw 1/1 Running 0 3m46s 10.88.0.102 host-8 <none> <none><br />
pod/web-5778c5c-h7bjh 1/1 Running 0 7s 10.88.0.104 host-8 <none> <none><br />
pod/web-5778c5c-nqxhs 1/1 Running 0 7s 10.88.0.105 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/redis NodePort 10.110.219.99 <none> 6379:30921/TCP 3m46s app=redis<br />
service/web NodePort 10.103.86.45 <none> 8080:31434/TCP 3m46s app=web<br />
<br />
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR<br />
deployment.apps/redis 1/1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis<br />
deployment.apps/web 3/3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web<br />
<br />
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR<br />
replicaset.apps/redis-7595cd897c 1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis,pod-template-hash=7595cd897c<br />
replicaset.apps/web-5778c5c 3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web,pod-template-hash=5778c5c<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
Для проверки работы POD'а запустите (если не сделали это ранее) контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>web.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://web.default:8080<br />
</pre><br />
<pre><br />
counter=3<br />
</pre><br />
<br />
<br />
''Обратите внимание, что в отличие от разворачивания Pod (домен <code>counter.default</code>) идет обращение к домену <code>web.default</code>.''<br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:31434<br />
</pre> <br />
<pre><br />
counter=4<br />
</pre><br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Deployment/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "redis" deleted<br />
service "web" deleted<br />
deployment.apps "redis" deleted<br />
deployment.apps "web" deleted<br />
</pre><br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
При генерации для rootless-kubernetes укажите при вызове команды <code>podman-compose-to-kube</code> имя пользователя флагом <code>-u</code> (<code>--user</code>) имя пользователя под которым работает <code>kubernetes</code> (и имя группы флагом <code>-п</code> (<code>--group</code>), если имя группа отличается от имени пользователя).<br />
Например, если <code>kubernetes</code> работает под пользователем <code>u7s-admin</code> команда генерации <code>Deployment-разворачивания</code> выглядит так:<br />
<pre><br />
podman-compose-to-kube -u u7s-admin -t d pod_counter docker-compose.yaml<br />
</pre><br />
<br />
При указании флага <code>-u</code> для создаваемых в локальной файловой системе томов в качестве владельцев устанавливаются указанные в параметрах пользователь и группа.<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> в рамках пользователя `u7s-admin` хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>.<br />
<br />
Если образ был собран в рамках пользователя `u7s-admin`: <br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
<br />
Если образ был собран в рамках пользователя `root`: <br />
<pre><br />
# skopeo copy containers-storage:[/var/lib/containers/storage/]localhost/hello-py-aioweb containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====<br />
<br />
В <code>rootless-режиме</code> все создаваемые Nodeport-порты остаются в namespace пользователя от имени которого запущен <code>kubernetes</code>.<br />
Для проброса портов наружу необходимо зайти в <code>namepspace</code> пользователя командой:<br />
<pre><br />
machinectl shell u7s-admin@ /usr/libexec/podsec/u7s/bin/nsenter_u7s<br />
</pre><br />
<pre><br />
[INFO] Entering RootlessKit namespaces: OK<br />
[root@host ~]# <br />
</pre><br />
и сделать проброс портов:<br />
<pre><br />
# rootlessctl \<br />
--socket /run/user/989/usernetes/rootlesskit/api.sock <br />
add-ports "0.0.0.0:30748:30748/tcp"<br />
</pre><br />
<pre><br />
10<br />
</pre><br />
Где:<br />
* <code>989</code> - идентификатор (uid) пользователя <code>u7s-admin</code>;<br />
* <code>30748</code> - номер пробрасываемого порта.</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78146Podman-compose-to-kube2024-01-27T18:14:50Z<p>Kaf: /* Проброс внешних портов на узле */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
'''Файлы описания Deployment-решения redis.yml, web.yml'''<br />
<br />
Файлы описания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно перед запуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как при Deployment-разворачивания контейнеры разворачиваются в отдельных POD'ах<br />
и оба имеют порты, то для каждого из них генерируются отдельный файл описания сервисов <code>Service/redis.yml</code>, <code>Service/web.yml</code>.<br />
<br />
Файл <code>redis.yml</code> описания сервиса <code>redis</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: redis<br />
name: redis<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 30921<br />
port: 6379<br />
targetPort: 6379<br />
selector:<br />
app: redis<br />
type: NodePort<br />
</pre><br />
Если к сервису <code>Service/web.yml</code> не необходимости обращении извне елемент <code>spec.ports[0].nodePort</code> можно удалить.<br />
<br />
Файл <code>web.yml</code> описания сервиса <code>web</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: web<br />
name: web<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '8080'<br />
nodePort: 31434<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: web<br />
type: NodePort<br />
</pre><br />
<br />
'''Подкаталоги <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code>'''<br />
Структура и содержание подкаталогов <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code> разворачивания <code>Deployment</code> совпадает с разворачиванием <code>Pod</code>, описанное выше.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>Deployment-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Deployment/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/redis created<br />
service/web created<br />
deployment.apps/redis created<br />
deployment.apps/web created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Deployment/</code>.<br />
<br />
При необходимости Вы можете реплицировать (например в количестве 3) Deployment web командой:<br />
<pre><br />
kubectl scale --replicas=3 deployment web<br />
</pre><br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/redis-7595cd897c-894dd 1/1 Running 0 3m46s 10.88.0.103 host-8 <none> <none><br />
pod/web-5778c5c-b8gcw 1/1 Running 0 3m46s 10.88.0.102 host-8 <none> <none><br />
pod/web-5778c5c-h7bjh 1/1 Running 0 7s 10.88.0.104 host-8 <none> <none><br />
pod/web-5778c5c-nqxhs 1/1 Running 0 7s 10.88.0.105 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/redis NodePort 10.110.219.99 <none> 6379:30921/TCP 3m46s app=redis<br />
service/web NodePort 10.103.86.45 <none> 8080:31434/TCP 3m46s app=web<br />
<br />
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR<br />
deployment.apps/redis 1/1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis<br />
deployment.apps/web 3/3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web<br />
<br />
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR<br />
replicaset.apps/redis-7595cd897c 1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis,pod-template-hash=7595cd897c<br />
replicaset.apps/web-5778c5c 3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web,pod-template-hash=5778c5c<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
Для проверки работы POD'а запустите (если не сделали это ранее) контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>web.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://web.default:8080<br />
</pre><br />
<pre><br />
counter=3<br />
</pre><br />
<br />
<br />
''Обратите внимание, что в отличие от разворачивания Pod (домен <code>counter.default</code>) идет обращение к домену <code>web.default</code>.''<br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:31434<br />
</pre> <br />
<pre><br />
counter=4<br />
</pre><br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Deployment/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "redis" deleted<br />
service "web" deleted<br />
deployment.apps "redis" deleted<br />
deployment.apps "web" deleted<br />
</pre><br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
При генерации для rootless-kubernetes укажите при вызове команды <code>podman-compose-to-kube</code> имя пользователя флагом <code>-u</code> (<code>--user</code>) имя пользователя под которым работает <code>kubernetes</code> (и имя группы флагом <code>-п</code> (<code>--group</code>), если имя группа отличается от имени пользователя).<br />
Например, если <code>kubernetes</code> работает под пользователем <code>u7s-admin</code> команда генерации <code>Deployment-разворачивания</code> выглядит так:<br />
<pre><br />
podman-compose-to-kube -u u7s-admin -t d pod_counter docker-compose.yaml<br />
</pre><br />
<br />
При указании флага <code>-u</code> для создаваемых в локальной файловой системе томов в качестве владельцев устанавливаются указанные в параметрах пользователь и группа.<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====<br />
<br />
В <code>rootless-режиме</code> все создаваемые Nodeport-порты остаются в namespace пользователя от имени которого запущен <code>kubernetes</code>.<br />
Для проброса портов наружу необходимо зайти в <code>namepspace</code> пользователя командой:<br />
<pre><br />
machinectl shell u7s-admin@ /usr/libexec/podsec/u7s/bin/nsenter_u7s<br />
</pre><br />
<pre><br />
[INFO] Entering RootlessKit namespaces: OK<br />
[root@host ~]# <br />
</pre><br />
и сделать проброс портов:<br />
<pre><br />
# rootlessctl \<br />
--socket /run/user/989/usernetes/rootlesskit/api.sock <br />
add-ports "0.0.0.0:30748:30748/tcp"<br />
</pre><br />
<pre><br />
10<br />
</pre><br />
Где:<br />
* <code>989</code> - идентификатор (uid) пользователя <code>u7s-admin</code>;<br />
* <code>30748</code> - номер пробрасываемого порта.</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78145Podman-compose-to-kube2024-01-27T18:12:58Z<p>Kaf: /* Проброс внешних портов на узле */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
'''Файлы описания Deployment-решения redis.yml, web.yml'''<br />
<br />
Файлы описания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно перед запуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как при Deployment-разворачивания контейнеры разворачиваются в отдельных POD'ах<br />
и оба имеют порты, то для каждого из них генерируются отдельный файл описания сервисов <code>Service/redis.yml</code>, <code>Service/web.yml</code>.<br />
<br />
Файл <code>redis.yml</code> описания сервиса <code>redis</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: redis<br />
name: redis<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 30921<br />
port: 6379<br />
targetPort: 6379<br />
selector:<br />
app: redis<br />
type: NodePort<br />
</pre><br />
Если к сервису <code>Service/web.yml</code> не необходимости обращении извне елемент <code>spec.ports[0].nodePort</code> можно удалить.<br />
<br />
Файл <code>web.yml</code> описания сервиса <code>web</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: web<br />
name: web<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '8080'<br />
nodePort: 31434<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: web<br />
type: NodePort<br />
</pre><br />
<br />
'''Подкаталоги <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code>'''<br />
Структура и содержание подкаталогов <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code> разворачивания <code>Deployment</code> совпадает с разворачиванием <code>Pod</code>, описанное выше.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>Deployment-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Deployment/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/redis created<br />
service/web created<br />
deployment.apps/redis created<br />
deployment.apps/web created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Deployment/</code>.<br />
<br />
При необходимости Вы можете реплицировать (например в количестве 3) Deployment web командой:<br />
<pre><br />
kubectl scale --replicas=3 deployment web<br />
</pre><br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/redis-7595cd897c-894dd 1/1 Running 0 3m46s 10.88.0.103 host-8 <none> <none><br />
pod/web-5778c5c-b8gcw 1/1 Running 0 3m46s 10.88.0.102 host-8 <none> <none><br />
pod/web-5778c5c-h7bjh 1/1 Running 0 7s 10.88.0.104 host-8 <none> <none><br />
pod/web-5778c5c-nqxhs 1/1 Running 0 7s 10.88.0.105 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/redis NodePort 10.110.219.99 <none> 6379:30921/TCP 3m46s app=redis<br />
service/web NodePort 10.103.86.45 <none> 8080:31434/TCP 3m46s app=web<br />
<br />
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR<br />
deployment.apps/redis 1/1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis<br />
deployment.apps/web 3/3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web<br />
<br />
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR<br />
replicaset.apps/redis-7595cd897c 1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis,pod-template-hash=7595cd897c<br />
replicaset.apps/web-5778c5c 3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web,pod-template-hash=5778c5c<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
Для проверки работы POD'а запустите (если не сделали это ранее) контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>web.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://web.default:8080<br />
</pre><br />
<pre><br />
counter=3<br />
</pre><br />
<br />
<br />
''Обратите внимание, что в отличие от разворачивания Pod (домен <code>counter.default</code>) идет обращение к домену <code>web.default</code>.''<br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:31434<br />
</pre> <br />
<pre><br />
counter=4<br />
</pre><br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Deployment/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "redis" deleted<br />
service "web" deleted<br />
deployment.apps "redis" deleted<br />
deployment.apps "web" deleted<br />
</pre><br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
При генерации для rootless-kubernetes укажите при вызове команды <code>podman-compose-to-kube</code> имя пользователя флагом <code>-u</code> (<code>--user</code>) имя пользователя под которым работает <code>kubernetes</code> (и имя группы флагом <code>-п</code> (<code>--group</code>), если имя группа отличается от имени пользователя).<br />
Например, если <code>kubernetes</code> работает под пользователем <code>u7s-admin</code> команда генерации <code>Deployment-разворачивания</code> выглядит так:<br />
<pre><br />
podman-compose-to-kube -u u7s-admin -t d pod_counter docker-compose.yaml<br />
</pre><br />
<br />
При указании флага <code>-u</code> для создаваемых в локальной файловой системе томов в качестве владельцев устанавливаются указанные в параметрах пользователь и группа.<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====<br />
<br />
В <code>rootless-режиме</code> все создаваемые Nodeport-порты остаются в namespace пользователя от имени которого запущен <code>kubernetes</code>.<br />
Для проброса портов наружу необходимо зайти в <code>namepspace</code> пользователя командой:<br />
<pre><br />
machinectl shell u7s-admin@ /usr/libexec/podsec/u7s/bin/nsenter_u7s<br />
[INFO] Entering RootlessKit namespaces: OK<br />
[root@host ~]# <br />
</pre><br />
и сделать проброс портов:<br />
<pre><br />
# rootlessctl \<br />
--socket /run/user/989/usernetes/rootlesskit/api.sock <br />
add-ports "0.0.0.0:30748:30748/tcp"<br />
</pre><br />
Где:<br />
* <code>989</code> - идентификатор (uid) пользователя <code>u7s-admin</code>;<br />
* <code>30748</code> - номер пробрасываемого порта.</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78144Podman-compose-to-kube2024-01-27T18:02:05Z<p>Kaf: /* Указание имени пользователя при генерации манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
'''Файлы описания Deployment-решения redis.yml, web.yml'''<br />
<br />
Файлы описания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно перед запуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как при Deployment-разворачивания контейнеры разворачиваются в отдельных POD'ах<br />
и оба имеют порты, то для каждого из них генерируются отдельный файл описания сервисов <code>Service/redis.yml</code>, <code>Service/web.yml</code>.<br />
<br />
Файл <code>redis.yml</code> описания сервиса <code>redis</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: redis<br />
name: redis<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 30921<br />
port: 6379<br />
targetPort: 6379<br />
selector:<br />
app: redis<br />
type: NodePort<br />
</pre><br />
Если к сервису <code>Service/web.yml</code> не необходимости обращении извне елемент <code>spec.ports[0].nodePort</code> можно удалить.<br />
<br />
Файл <code>web.yml</code> описания сервиса <code>web</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: web<br />
name: web<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '8080'<br />
nodePort: 31434<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: web<br />
type: NodePort<br />
</pre><br />
<br />
'''Подкаталоги <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code>'''<br />
Структура и содержание подкаталогов <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code> разворачивания <code>Deployment</code> совпадает с разворачиванием <code>Pod</code>, описанное выше.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>Deployment-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Deployment/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/redis created<br />
service/web created<br />
deployment.apps/redis created<br />
deployment.apps/web created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Deployment/</code>.<br />
<br />
При необходимости Вы можете реплицировать (например в количестве 3) Deployment web командой:<br />
<pre><br />
kubectl scale --replicas=3 deployment web<br />
</pre><br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/redis-7595cd897c-894dd 1/1 Running 0 3m46s 10.88.0.103 host-8 <none> <none><br />
pod/web-5778c5c-b8gcw 1/1 Running 0 3m46s 10.88.0.102 host-8 <none> <none><br />
pod/web-5778c5c-h7bjh 1/1 Running 0 7s 10.88.0.104 host-8 <none> <none><br />
pod/web-5778c5c-nqxhs 1/1 Running 0 7s 10.88.0.105 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/redis NodePort 10.110.219.99 <none> 6379:30921/TCP 3m46s app=redis<br />
service/web NodePort 10.103.86.45 <none> 8080:31434/TCP 3m46s app=web<br />
<br />
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR<br />
deployment.apps/redis 1/1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis<br />
deployment.apps/web 3/3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web<br />
<br />
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR<br />
replicaset.apps/redis-7595cd897c 1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis,pod-template-hash=7595cd897c<br />
replicaset.apps/web-5778c5c 3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web,pod-template-hash=5778c5c<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
Для проверки работы POD'а запустите (если не сделали это ранее) контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>web.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://web.default:8080<br />
</pre><br />
<pre><br />
counter=3<br />
</pre><br />
<br />
<br />
''Обратите внимание, что в отличие от разворачивания Pod (домен <code>counter.default</code>) идет обращение к домену <code>web.default</code>.''<br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:31434<br />
</pre> <br />
<pre><br />
counter=4<br />
</pre><br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Deployment/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "redis" deleted<br />
service "web" deleted<br />
deployment.apps "redis" deleted<br />
deployment.apps "web" deleted<br />
</pre><br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
При генерации для rootless-kubernetes укажите при вызове команды <code>podman-compose-to-kube</code> имя пользователя флагом <code>-u</code> (<code>--user</code>) имя пользователя под которым работает <code>kubernetes</code> (и имя группы флагом <code>-п</code> (<code>--group</code>), если имя группа отличается от имени пользователя).<br />
Например, если <code>kubernetes</code> работает под пользователем <code>u7s-admin</code> команда генерации <code>Deployment-разворачивания</code> выглядит так:<br />
<pre><br />
podman-compose-to-kube -u u7s-admin -t d pod_counter docker-compose.yaml<br />
</pre><br />
<br />
При указании флага <code>-u</code> для создаваемых в локальной файловой системе томов в качестве владельцев устанавливаются указанные в параметрах пользователь и группа.<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78143Podman-compose-to-kube2024-01-27T17:52:19Z<p>Kaf: /* Останов манифестов Deployment'а */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
'''Файлы описания Deployment-решения redis.yml, web.yml'''<br />
<br />
Файлы описания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно перед запуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как при Deployment-разворачивания контейнеры разворачиваются в отдельных POD'ах<br />
и оба имеют порты, то для каждого из них генерируются отдельный файл описания сервисов <code>Service/redis.yml</code>, <code>Service/web.yml</code>.<br />
<br />
Файл <code>redis.yml</code> описания сервиса <code>redis</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: redis<br />
name: redis<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 30921<br />
port: 6379<br />
targetPort: 6379<br />
selector:<br />
app: redis<br />
type: NodePort<br />
</pre><br />
Если к сервису <code>Service/web.yml</code> не необходимости обращении извне елемент <code>spec.ports[0].nodePort</code> можно удалить.<br />
<br />
Файл <code>web.yml</code> описания сервиса <code>web</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: web<br />
name: web<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '8080'<br />
nodePort: 31434<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: web<br />
type: NodePort<br />
</pre><br />
<br />
'''Подкаталоги <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code>'''<br />
Структура и содержание подкаталогов <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code> разворачивания <code>Deployment</code> совпадает с разворачиванием <code>Pod</code>, описанное выше.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>Deployment-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Deployment/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/redis created<br />
service/web created<br />
deployment.apps/redis created<br />
deployment.apps/web created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Deployment/</code>.<br />
<br />
При необходимости Вы можете реплицировать (например в количестве 3) Deployment web командой:<br />
<pre><br />
kubectl scale --replicas=3 deployment web<br />
</pre><br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/redis-7595cd897c-894dd 1/1 Running 0 3m46s 10.88.0.103 host-8 <none> <none><br />
pod/web-5778c5c-b8gcw 1/1 Running 0 3m46s 10.88.0.102 host-8 <none> <none><br />
pod/web-5778c5c-h7bjh 1/1 Running 0 7s 10.88.0.104 host-8 <none> <none><br />
pod/web-5778c5c-nqxhs 1/1 Running 0 7s 10.88.0.105 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/redis NodePort 10.110.219.99 <none> 6379:30921/TCP 3m46s app=redis<br />
service/web NodePort 10.103.86.45 <none> 8080:31434/TCP 3m46s app=web<br />
<br />
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR<br />
deployment.apps/redis 1/1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis<br />
deployment.apps/web 3/3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web<br />
<br />
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR<br />
replicaset.apps/redis-7595cd897c 1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis,pod-template-hash=7595cd897c<br />
replicaset.apps/web-5778c5c 3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web,pod-template-hash=5778c5c<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
Для проверки работы POD'а запустите (если не сделали это ранее) контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>web.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://web.default:8080<br />
</pre><br />
<pre><br />
counter=3<br />
</pre><br />
<br />
<br />
''Обратите внимание, что в отличие от разворачивания Pod (домен <code>counter.default</code>) идет обращение к домену <code>web.default</code>.''<br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:31434<br />
</pre> <br />
<pre><br />
counter=4<br />
</pre><br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Deployment/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "redis" deleted<br />
service "web" deleted<br />
deployment.apps "redis" deleted<br />
deployment.apps "web" deleted<br />
</pre><br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78142Podman-compose-to-kube2024-01-27T17:50:47Z<p>Kaf: /* Проверка работы Deploymant'а */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
'''Файлы описания Deployment-решения redis.yml, web.yml'''<br />
<br />
Файлы описания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно перед запуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как при Deployment-разворачивания контейнеры разворачиваются в отдельных POD'ах<br />
и оба имеют порты, то для каждого из них генерируются отдельный файл описания сервисов <code>Service/redis.yml</code>, <code>Service/web.yml</code>.<br />
<br />
Файл <code>redis.yml</code> описания сервиса <code>redis</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: redis<br />
name: redis<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 30921<br />
port: 6379<br />
targetPort: 6379<br />
selector:<br />
app: redis<br />
type: NodePort<br />
</pre><br />
Если к сервису <code>Service/web.yml</code> не необходимости обращении извне елемент <code>spec.ports[0].nodePort</code> можно удалить.<br />
<br />
Файл <code>web.yml</code> описания сервиса <code>web</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: web<br />
name: web<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '8080'<br />
nodePort: 31434<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: web<br />
type: NodePort<br />
</pre><br />
<br />
'''Подкаталоги <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code>'''<br />
Структура и содержание подкаталогов <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code> разворачивания <code>Deployment</code> совпадает с разворачиванием <code>Pod</code>, описанное выше.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>Deployment-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Deployment/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/redis created<br />
service/web created<br />
deployment.apps/redis created<br />
deployment.apps/web created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Deployment/</code>.<br />
<br />
При необходимости Вы можете реплицировать (например в количестве 3) Deployment web командой:<br />
<pre><br />
kubectl scale --replicas=3 deployment web<br />
</pre><br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/redis-7595cd897c-894dd 1/1 Running 0 3m46s 10.88.0.103 host-8 <none> <none><br />
pod/web-5778c5c-b8gcw 1/1 Running 0 3m46s 10.88.0.102 host-8 <none> <none><br />
pod/web-5778c5c-h7bjh 1/1 Running 0 7s 10.88.0.104 host-8 <none> <none><br />
pod/web-5778c5c-nqxhs 1/1 Running 0 7s 10.88.0.105 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/redis NodePort 10.110.219.99 <none> 6379:30921/TCP 3m46s app=redis<br />
service/web NodePort 10.103.86.45 <none> 8080:31434/TCP 3m46s app=web<br />
<br />
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR<br />
deployment.apps/redis 1/1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis<br />
deployment.apps/web 3/3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web<br />
<br />
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR<br />
replicaset.apps/redis-7595cd897c 1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis,pod-template-hash=7595cd897c<br />
replicaset.apps/web-5778c5c 3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web,pod-template-hash=5778c5c<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
Для проверки работы POD'а запустите (если не сделали это ранее) контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>web.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://web.default:8080<br />
</pre><br />
<pre><br />
counter=3<br />
</pre><br />
<br />
<br />
''Обратите внимание, что в отличие от разворачивания Pod (домен <code>counter.default</code>) идет обращение к домену <code>web.default</code>.''<br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:31434<br />
</pre> <br />
<pre><br />
counter=4<br />
</pre><br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78141Podman-compose-to-kube2024-01-27T17:43:32Z<p>Kaf: /* Запуск манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
'''Файлы описания Deployment-решения redis.yml, web.yml'''<br />
<br />
Файлы описания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно перед запуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как при Deployment-разворачивания контейнеры разворачиваются в отдельных POD'ах<br />
и оба имеют порты, то для каждого из них генерируются отдельный файл описания сервисов <code>Service/redis.yml</code>, <code>Service/web.yml</code>.<br />
<br />
Файл <code>redis.yml</code> описания сервиса <code>redis</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: redis<br />
name: redis<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 30921<br />
port: 6379<br />
targetPort: 6379<br />
selector:<br />
app: redis<br />
type: NodePort<br />
</pre><br />
Если к сервису <code>Service/web.yml</code> не необходимости обращении извне елемент <code>spec.ports[0].nodePort</code> можно удалить.<br />
<br />
Файл <code>web.yml</code> описания сервиса <code>web</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: web<br />
name: web<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '8080'<br />
nodePort: 31434<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: web<br />
type: NodePort<br />
</pre><br />
<br />
'''Подкаталоги <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code>'''<br />
Структура и содержание подкаталогов <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code> разворачивания <code>Deployment</code> совпадает с разворачиванием <code>Pod</code>, описанное выше.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>Deployment-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Deployment/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/redis created<br />
service/web created<br />
deployment.apps/redis created<br />
deployment.apps/web created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Deployment/</code>.<br />
<br />
При необходимости Вы можете реплицировать (например в количестве 3) Deployment web командой:<br />
<pre><br />
kubectl scale --replicas=3 deployment web<br />
</pre><br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/redis-7595cd897c-894dd 1/1 Running 0 3m46s 10.88.0.103 host-8 <none> <none><br />
pod/web-5778c5c-b8gcw 1/1 Running 0 3m46s 10.88.0.102 host-8 <none> <none><br />
pod/web-5778c5c-h7bjh 1/1 Running 0 7s 10.88.0.104 host-8 <none> <none><br />
pod/web-5778c5c-nqxhs 1/1 Running 0 7s 10.88.0.105 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/redis NodePort 10.110.219.99 <none> 6379:30921/TCP 3m46s app=redis<br />
service/web NodePort 10.103.86.45 <none> 8080:31434/TCP 3m46s app=web<br />
<br />
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR<br />
deployment.apps/redis 1/1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis<br />
deployment.apps/web 3/3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web<br />
<br />
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR<br />
replicaset.apps/redis-7595cd897c 1 1 1 3m46s counterredis1 docker.io/library/redis:alpine app=redis,pod-template-hash=7595cd897c<br />
replicaset.apps/web-5778c5c 3 3 3 3m46s counterweb1 localhost/hello-py-aioweb:latest app=web,pod-template-hash=5778c5c<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78140Podman-compose-to-kube2024-01-27T17:33:33Z<p>Kaf: /* Разворачивание в виде kubernetes Deployment */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
'''Файлы описания Deployment-решения redis.yml, web.yml'''<br />
<br />
Файлы описания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно перед запуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как при Deployment-разворачивания контейнеры разворачиваются в отдельных POD'ах<br />
и оба имеют порты, то для каждого из них генерируются отдельный файл описания сервисов <code>Service/redis.yml</code>, <code>Service/web.yml</code>.<br />
<br />
Файл <code>redis.yml</code> описания сервиса <code>redis</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: redis<br />
name: redis<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 30921<br />
port: 6379<br />
targetPort: 6379<br />
selector:<br />
app: redis<br />
type: NodePort<br />
</pre><br />
Если к сервису <code>Service/web.yml</code> не необходимости обращении извне елемент <code>spec.ports[0].nodePort</code> можно удалить.<br />
<br />
Файл <code>web.yml</code> описания сервиса <code>web</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T16:04:24Z'<br />
labels:<br />
app: web<br />
name: web<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '8080'<br />
nodePort: 31434<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: web<br />
type: NodePort<br />
</pre><br />
<br />
'''Подкаталоги <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code>'''<br />
Структура и содержание подкаталогов <code>PersistentVolumeClaim</code>, <code>PersistentVolume</code> разворачивания <code>Deployment</code> совпадает с разворачиванием <code>Pod</code>, описанное выше.<br />
<br />
====== Запуск манифестов ======<br />
<br />
<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78139Podman-compose-to-kube2024-01-27T17:16:46Z<p>Kaf: /* Генерация манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
Файлы разворачивания Deployment-решения помещаются в подкаталог <code>Deployment</code><br />
каталога POD'а <code>counter</code>.<br />
Так как при Deployment-разворачивании каждый контейнер может реплицироваться они помещаются в разные Deployment's, описываемые в YML-файлах <code>redis.yml</code>, <code>web.yml</code>.<br />
<br />
Разворачивание сервиса <code>redis</code>, файл <code>redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: redis<br />
labels:<br />
app: redis<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: redis<br />
template:<br />
metadata:<br />
labels:<br />
app: redis<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
</pre><br />
<br />
Описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в нулевом элементе <code>spec</code>, как и элемент описания внешнего тома <code>code>spec.template.volumes</code>. <br />
Так как контейнер имеет внешний том, примонтированный в режиме чтения-записи этот контейнер не может реплицироваться <code>spec.replicas: 1</code>. <br />
<br />
Разворачивание сервиса <code>web</code>, файл <code>web.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: apps/v1<br />
kind: Deployment<br />
metadata:<br />
name: web<br />
labels:<br />
app: web<br />
namespace: default<br />
spec:<br />
replicas: 1<br />
selector:<br />
matchLabels:<br />
app: web<br />
template:<br />
metadata:<br />
labels:<br />
app: web<br />
spec:<br />
containers:<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
</pre><br />
Как и для сервиса <code>redis</code> в сервисе <code>web</code> описание контейнера в элементе <code>spec.template.spec</code> совпадает с описанием в режиме POD'а в первом элементе <code>spec</code>.<br />
Так как контейнер не имеет внешних томов этот контейнер может реплицироваться до требуемых значений. Можно пережзапуском установить нужное число реплик в элементе <code>spec.replicas</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78136Podman-compose-to-kube2024-01-27T16:14:35Z<p>Kaf: /* Разворачивание в виде kubernetes Deployment */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для Deployment-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube -t d --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Формат вызова команды для генерации Deployment-разворачивания отличается наличием флага <code>-t d</code> (<code>--type=deployment</code>).<br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Deployment<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Deployment/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Deployment/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy files of the Deployment type:<br />
redis<br />
Add volume descriptions to the container<br />
Generate a deploy file manifests/default/counter/Deployment/redis.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/redis.yml<br />
web<br />
Generate a deploy file manifests/default/counter/Deployment/web.yml<br />
Add descriptions of the ports to the service<br />
Generate a service file manifests/default/counter/Deployment/Service/web.yml<br />
</pre><br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Deployment<br />
├── redis.yml<br />
├── web.yml<br />
├── Service<br />
│ ├── redis.yml<br />
│ └── web.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
<br />
<br />
====== Запуск манифестов ======<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78135Podman-compose-to-kube2024-01-27T16:14:10Z<p>Kaf: /* Генерация манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── Service<br />
│ └── counter.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── PersistentVolume<br />
└── default-counter-redis.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78134Podman-compose-to-kube2024-01-27T14:50:22Z<p>Kaf: /* Запуск манифестов POD'а */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
Проверьте назначение внешнего тома:<br />
<pre><br />
kubectl -n default get pvc <br />
</pre> <br />
<pre><br />
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE<br />
counter-redis Bound default-counter-redis 1Gi RWO manual 46s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78133Podman-compose-to-kube2024-01-27T14:45:10Z<p>Kaf: /* Останов манифестов POD'а */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
Для остановки работы POD'а набеоите команду:<br />
<pre><br />
kubectl delete -R -f manifests/default/counter/Pod/<br />
</pre><br />
<pre><br />
persistentvolume "default-counter-redis" deleted<br />
persistentvolumeclaim "counter-redis" deleted<br />
service "counter" deleted<br />
pod "counter" deleted<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78132Podman-compose-to-kube2024-01-27T14:41:19Z<p>Kaf: /* Запуск манифестов Deployment'а */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
===== Останов манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78131Podman-compose-to-kube2024-01-27T14:40:54Z<p>Kaf: /* Запуск манифестов POD'а */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Останов манифестов POD'а =====<br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
===== Запуск манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78130Podman-compose-to-kube2024-01-27T14:40:16Z<p>Kaf: /* Экспорт запущенного POD'а в kubernetes-манифесты и их запуск */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
===== Запуск манифестов POD'а =====<br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
===== Запуск манифестов Deployment'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78129Podman-compose-to-kube2024-01-27T14:38:02Z<p>Kaf: /* Запуск манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов POD'а ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78128Podman-compose-to-kube2024-01-27T14:34:46Z<p>Kaf: /* Разворачивание в виде kubernetes Deployment */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
===== Проверка работы Deploymant'а =====<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78127Podman-compose-to-kube2024-01-27T14:33:06Z<p>Kaf: /* Проброс внешних портов на узле */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Проброс внешних портов на узле ====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78126Podman-compose-to-kube2024-01-27T14:32:23Z<p>Kaf: /* Особенности запуска в rootless окружении */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
===== Указание имени пользователя при генерации манифестов =====<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
===== Проброс внешних портов на узле =====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78125Podman-compose-to-kube2024-01-27T14:29:45Z<p>Kaf: /* Проверка работы POD'а */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://&lt;IP>:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78124Podman-compose-to-kube2024-01-27T14:29:05Z<p>Kaf: /* Проверка работы POD'а */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <code>praqma/network-multitool</code>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>counter.default</code> из конейнера:<br />
<pre><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
</pre><br />
<pre><br />
counter=1<br />
</pre><br />
<br />
Работу можно проверить также обратившись к внешнему порту узла, на котором запущен <code>POD</code>:<br />
<pre><br />
curl http://localhost:30748<br />
</pre> <br />
<pre><br />
counter=2<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78123Podman-compose-to-kube2024-01-27T14:22:31Z<p>Kaf: /* Запуск манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all -o wide<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES<br />
pod/counter 2/2 Running 0 22m 10.88.0.99 host-8 <none> <none><br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 22m app=counter<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <pre>praqma/network-multitool</pre>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>web.default</code> из конейнера:<br />
<code><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
<br />
</code><br />
<code><br />
counter=1<br />
</code><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78122Podman-compose-to-kube2024-01-27T14:20:52Z<p>Kaf: /* Разворачивание в виде kubernetes POD */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE<br />
pod/counter 2/2 Running 0 4s<br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 4s<br />
</pre><br />
<br />
====== Проверка работы POD'а ======<br />
<br />
Для проверки работы POD'а запустите контейнер от образа <pre>praqma/network-multitool</pre>:<br />
<pre><br />
kubectl run multitool --image=praqma/network-multitool<br />
</pre><br />
<pre><br />
pod/multitool created<br />
</pre><br />
<br />
Сделайте запрос на сервис <code>web.default</code> из конейнера:<br />
<code><br />
kubectl exec -it pod/multitool -- curl http://counter.default:8080<br />
<br />
</code><br />
<code><br />
counter=1<br />
</code><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78121Podman-compose-to-kube2024-01-27T14:02:10Z<p>Kaf: /* Запуск манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
Запуск <code>POD-манифестов</code> производится командой:<br />
<pre><br />
kubectl apply -R -f manifests/default/counter/Pod/<br />
</pre> <br />
<pre><br />
persistentvolume/default-counter-redis created<br />
persistentvolumeclaim/counter-redis created<br />
service/counter created<br />
pod/counter created<br />
</pre><br />
<br />
Команда рекурсивно выполнить все YML-файлы каталога <code>manifests/default/counter/Pod/</code>.<br />
<br />
Состояние контейнера и сервиса можно посмотреть командой<br />
<pre><br />
kubectl -n default get all<br />
</pre><br />
<pre><br />
NAME READY STATUS RESTARTS AGE<br />
pod/counter 2/2 Running 0 4s<br />
<br />
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE<br />
service/counter NodePort 10.108.81.8 <none> 6379:30031/TCP,8080:30748/TCP 4s<br />
</pre><br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78120Podman-compose-to-kube2024-01-27T13:53:08Z<p>Kaf: /* Разворачивание в виде kubernetes POD */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
Генерация манифестов производится командой <code>podman-compose-to-kube</code>.<br />
Формат ее вызова:<br />
<pre><br />
podman-compose-to-kube \<br />
[--type(-t) &lt;deployment type>]\<br />
[--namespace(-n) &lt;namespace>]<br />
[--dir(-d) &lt;manifests_directory>]\<br />
[--pvpath &lt;PersistentVolume_directory>] \<br />
[--user &lt;rootless_user>]\<br />
[--group &lt;rootless_group>]\<br />
[--debug &lt;debug_level>]\<br />
&lt;POD_name>\<br />
&lt;docker-compose_file_name><br />
</pre><br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78119Podman-compose-to-kube2024-01-27T13:50:15Z<p>Kaf: /* Генерация манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
Для каждого запроса на том в каталоге <code>PersistentVolume</code> формируется описание тома на локальном диске узла. Файл <code>default-counter-redis.yml</code> содержит следующую информацию:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolume<br />
metadata:<br />
name: default-counter-redis<br />
labels:<br />
type: local<br />
spec:<br />
storageClassName: manual<br />
claimRef:<br />
name: counter-redis<br />
namespace: default<br />
capacity:<br />
storage: 1Gi<br />
accessModes:<br />
- ReadWriteOnce<br />
hostPath:<br />
path: /mnt/PersistentVolumes/default/counter-redis<br />
</pre><br />
<br />
Для запроса (<code>claimRef</code>) с именем <code>counter-redis</code> <br />
в каталоге <code>/mnt/PersistentVolumes/default/counter-redis</code> выделяется <code>1Gi</code> дискового пространства.<br />
Имя корневого каталог томов <code>/mnt/PersistentVolumes/</code> можно изменить указав друглй каталог в параметре <code>--pvpath</code>.<br />
<br />
====== Запуск манифестов ======<br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78118Podman-compose-to-kube2024-01-27T13:43:07Z<p>Kaf: /* Генерация манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
'''Файл описания Pod-контейнера counter.yml'''<br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter</code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
'''Подкаталог описания kubernet-сервиса <code>Service</code>'''<br />
<br />
Так как в рамках разворачивания запускается всего один <code>POD</code> подкаталог описания kubernet-сервиса <code>Service</code> содержит всего один файл <code>counter.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.0-alt1<br />
apiVersion: v1<br />
kind: Service<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
ports:<br />
- name: '6379'<br />
nodePort: 32717<br />
port: 6379<br />
targetPort: 6379<br />
- name: '8080'<br />
nodePort: 31703<br />
port: 8080<br />
targetPort: 8080<br />
selector:<br />
app: counter<br />
type: NodePort<br />
</pre><br />
<br />
Файл описывает для <code>POD</code>'а с именем <code>counter</code> в <code>namespace: default</code> два порта для внешнего доступа:<br />
* <code>6379</code> - с node-портом для внешнего доступа <code>32717</code>; <br />
* <code>8080</code> - с node-портом для внешнего доступа <code>31703</code>.<br />
<br />
Если внешний доступ к контейнеру <code>counterredis1</code> не требуется описание порта <code>6379</code> можно удалить. <br />
<br />
'''Подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома'''<br />
<br />
docker-compose YML файл содержит описание только одного внешнего тома для сервиса <code>redis</code>. Данное описание генерирует запрос на выделение тома, содержащееся в файле <code>counter-redis.yml</code>:<br />
<pre><br />
# Created with podman-compose-to-kube 1.0.6-alt1<br />
apiVersion: v1<br />
kind: PersistentVolumeClaim<br />
metadata:<br />
annotations:<br />
volume.podman.io/driver: local<br />
creationTimestamp: '2024-01-27T11:05:27Z'<br />
name: counter-redis<br />
namespace: default<br />
spec:<br />
accessModes:<br />
- ReadWriteOnce<br />
resources:<br />
requests:<br />
storage: 1Gi<br />
storageClassName: manual<br />
</pre><br />
<br />
Файл для запроса <code>counter-redis</code> в <code>namespace: default</code> запрашивает том объемом <code>1Gi</code>.<br />
<br />
'''Подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания'''<br />
<br />
<code></code><br />
<br />
====== Запуск манифестов ======<br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78117Podman-compose-to-kube2024-01-27T13:05:33Z<p>Kaf: /* Генерация манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
Файл описания Pod-контейнера <code>counter.yml</code> выглядит следующим образом:<br />
<pre><br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
creationTimestamp: '2024-01-27T11:05:26Z'<br />
labels:<br />
app: counter<br />
name: counter<br />
namespace: default<br />
spec:<br />
containers:<br />
- args:<br />
- redis-server<br />
- --appendonly<br />
- 'yes'<br />
- --notify-keyspace-events<br />
- Ex<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
ports:<br />
- containerPort: 6379<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter-redis-pvc<br />
- env:<br />
- name: REDIS_HOST<br />
value: redis<br />
- name: REDIS_PORT<br />
value: '6379'<br />
image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
securityContext:<br />
readOnlyRootFilesystem: true<br />
volumes:<br />
- name: counter-redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter-redis<br />
hostAliases:<br />
- ip: 127.0.0.1<br />
hostnames:<br />
- redis<br />
- web<br />
</pre><br />
Файл описывает в <code>namespace: default</code> в POD'е с именем <code><counter/code> два контейнера: <code>counterredis1</code>, <code>counterweb1</code>.<br />
<br />
Контейнер <code>counterredis1</code> принимает запросы по порту <code>6379</code> и монтирует каталог <code>/data</code> на том, получаемый по запросу (<code>PersisnentVolumeClaim</code>) с именем (<code>claimName</code>) <code>counter-redis</code>.<br />
<br />
Контейнер <code>counterweb1</code> принимает запросы по порту <code>8080</code>. В его среду экспортируются две переменные: <code>REDIS_HOST</code> и <code>REDIS_PORT</code>.<br />
<br />
Так как в разворачивании типа <code>POD</code> оба контейнера стартуют в одном <code>POD</code>'е с локальным адресом <code>127.0.0.1</code>, к YML-файлу добавляется описание <code>hostAliases</code>, привязывающий короткие DNS-имена <code>web</code>, <code>redis</code> к локальному адресу <code>127.0.0.1</code>. Таким образом контейнер <code>redis</code> доступен из контейнера <code>web</code> под именем <code>redis</code> через локальный интерфейс <code>lo</code> <code>POD</code>'а.<br />
<br />
<br />
<code></code><br />
<br />
====== Запуск манифестов ======<br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78116Podman-compose-to-kube2024-01-27T12:47:22Z<p>Kaf: /* Установка ПО, создание пользователей, разворачивание kubernetes */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>, <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
<br />
<br />
<br />
<br />
<br />
<code></code><br />
<br />
====== Запуск манифестов ======<br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78115Podman-compose-to-kube2024-01-27T11:43:13Z<p>Kaf: /* Генерация манифестов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>б <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre><br />
<br />
''Если в выводе шагов генерации нет необходимости флаг <code>--debug=1</code> можно опустить.''<br />
<br />
Первый параметр <code>pod_counter</code> указывает имя поднятого <code>podman-POD</code>'а. Второй <code>docker-compose.yaml</code> - имя YAML-файла из которого поднят контейнер.<br />
<br />
После вызова команды в текущем каталоге создастся подкаталог <code>manifests</code> следующей структуры:<br />
<pre><br />
manifests/<br />
└── default<br />
└── counter<br />
└── Pod<br />
├── counter.yml<br />
├── PersistentVolume<br />
│ └── default-counter-redis.yml<br />
├── PersistentVolumeClaim<br />
│ └── counter-redis.yml<br />
└── Service<br />
└── counter.yml<br />
</pre><br />
На первом уровне создастся каталог <code>default</code> имя которого задает <code>kubernetes-namespace</code> в котором будет запускаться <code>POD</code>.<br />
<br />
В подкаталоге <code>default</code> создается подкаталог <code>counter</code> имя которого берется из имени генерируемого <code>POD</code>'а отбрасыванием префикса <code>pod_</code>.<br />
<br />
В подкаталоге <code>counter</code> создается подкаталог по имени разворачивания - <code>Pod</code>.<br />
<br />
В каталоге типа разворачивания <code>Pod</code> генерируются:<br />
* файл описания Pod-контейнера <code>counter.yml</code>;<br />
* подкаталог описания kubernet-сервиса <code>Service</code><br />
* подкаталог <code>PersistentVolumeClaim</code> описания kubernet-запроса на монтируемые тома <br />
* подкаталог <code>PersistentVolume</code> описания томов для данного разворачивания. <br />
<br />
<br />
<br />
<br />
<br />
<br />
<code></code><br />
<br />
====== Запуск манифестов ======<br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78114Podman-compose-to-kube2024-01-27T11:07:26Z<p>Kaf: /* Разворачивание в виде kubernetes POD'а */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>б <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
Генерация манифестов для POD-разворачивания производится командой:<br />
<pre><br />
podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml<br />
</pre><br />
<pre><br />
Generate a POD manifest based on the specified POD<br />
Generate a list of scalar yml elements ending with name(Name)<br />
Generate a jq request to replace symbols _ with symbols - in selected elements<br />
Generate list of services in docker-compose file<br />
Add descriptions of the environment variables to the container web<br />
Removing the deployment directory manifests/default/counter/Pod<br />
Generate common POD YML file<br />
Generate PersistentVolumeClaims and PersistentVolumes:<br />
manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml<br />
manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml<br />
/mnt/PersistentVolumes/default/counter-redis<br />
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:<br />
</pre> <br />
<br />
====== Запуск манифестов ======<br />
<br />
==== Разворачивание в виде kubernetes Deployment ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ======<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78113Podman-compose-to-kube2024-01-27T10:59:20Z<p>Kaf: /* Экспорт развернутого стека в kubernetes-манифесты */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>б <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт запущенного POD'а в kubernetes-манифесты и их запуск===<br />
<br />
==== Разворачивание в виде kubernetes POD'а ====<br />
<br />
====== Генерация манифестов ====== <br />
<br />
====== Запуск манифестов ====== <br />
<br />
<br />
<br />
=== Особенности запуска в rootless окружении ===<br />
<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre></div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78112Podman-compose-to-kube2024-01-27T10:49:04Z<p>Kaf: /* Запуск стека сервисов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>б <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
</pre><br />
<pre><br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
</pre><br />
<pre><br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
</pre><br />
<pre><br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
</pre><br />
<pre><br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт развернутого стека в kubernetes-манифесты ===<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Формирование kubernetes-манифестов ====<br />
<br />
Для формирования запустите команду:<br />
<pre><br />
# podman kube generate pod_counter<br />
</pre><br />
<pre><br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
...<br />
name: podcounter<br />
spec:<br />
containers:<br />
...<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter_redis-pvc<br />
<br />
- image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
hostPort: 8080<br />
...<br />
volumes:<br />
- name: counter_redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter_redis<br />
</pre><br />
<br />
===== Запуск kubernetes-контейнеров =====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78111Podman-compose-to-kube2024-01-27T10:43:45Z<p>Kaf: /* Разворачивание стека сервисов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>б <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
В каталогн присутсвует файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо уточнить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт развернутого стека в kubernetes-манифесты ===<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Формирование kubernetes-манифестов ====<br />
<br />
Для формирования запустите команду:<br />
<pre><br />
# podman kube generate pod_counter<br />
</pre><br />
<pre><br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
...<br />
name: podcounter<br />
spec:<br />
containers:<br />
...<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter_redis-pvc<br />
<br />
- image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
hostPort: 8080<br />
...<br />
volumes:<br />
- name: counter_redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter_redis<br />
</pre><br />
<br />
===== Запуск kubernetes-контейнеров =====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78110Podman-compose-to-kube2024-01-27T10:36:24Z<p>Kaf: /* Описание стека сервисов */</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>б <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
Отредактируйте файл <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
<br />
В файл внесены два изменения:<br />
# В сервис <code>redis</code> добавлено описание порта <code>6379</code>.<br />
# В сервис <code>web</code> добавлено описание переменной <code>REDIS_PORT: 6379</code>.<br />
<br />
Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме <code>Deployment</code>.<br />
<br />
Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется <code>YML-файл описания kubernet-сервиса</code> и <code>redis-контейнер</code> будет недоступен из контейнера <code>web</code>.<br />
<br />
Второе изменение связано с тем, что в режиме <code>Deployment</code> в сконтейнер <code>web</code> экпортируется переменная <code>REDIS_PORT</code> в формате <code>http://<ip>:&lt;port></code>. Приложение <code>App/web.py</code> обрабатывает это значение в формате <code>&lt;port></code>.<br />
<br />
В сервисе <code>redis</code> запускается контейнер с томом <code>redis</code> и портом для внешнего доступа <code>6379</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо утоснить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
Отличия от исходного файла:<br />
<br />
* Добавлено описание порта <code>6379</code> в сервис <code>redis</code>. Это связано с тем, что файлы kubernetes-сервисов (<code>kind: Service</code>) создаются только для сервисов, имеющих порты. Если сервис создан не будет redis-контейнер для разворачивания типа <code>Deployment</code> (<code>kind: Deployment</code>) не получит DNS-имя в рамках kubernetes-кластера и будет недоступен из других контейнеров (<code>web-контейнера</code>). <br />
<br />
* Добавлено описание переменной (<code>environment</code>) <code>REDIS_PORT: 6379</code>. При отсутствии этой переменной в web-контейнер передается некорректное значение этой переменной формата <code>http://&lt.IP>:6379</code>, что вызывает ошибку в <code>python-коде App/web.py</code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт развернутого стека в kubernetes-манифесты ===<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Формирование kubernetes-манифестов ====<br />
<br />
Для формирования запустите команду:<br />
<pre><br />
# podman kube generate pod_counter<br />
</pre><br />
<pre><br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
...<br />
name: podcounter<br />
spec:<br />
containers:<br />
...<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter_redis-pvc<br />
<br />
- image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
hostPort: 8080<br />
...<br />
volumes:<br />
- name: counter_redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter_redis<br />
</pre><br />
<br />
===== Запуск kubernetes-контейнеров =====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kubernetes&diff=78108Podman-compose-to-kubernetes2024-01-27T08:40:26Z<p>Kaf: Kaf переименовал страницу Podman-compose-to-kubernetes в Podman-compose-to-kube: Более точное название</p>
<hr />
<div>#перенаправление [[Podman-compose-to-kube]]</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78107Podman-compose-to-kube2024-01-27T08:40:25Z<p>Kaf: Kaf переименовал страницу Podman-compose-to-kubernetes в Podman-compose-to-kube: Более точное название</p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>б <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
Каталог содержит в файле <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер и запускается томом <code>redis</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо утоснить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
Отличия от исходного файла:<br />
<br />
* Добавлено описание порта <code>6379</code> в сервис <code>redis</code>. Это связано с тем, что файлы kubernetes-сервисов (<code>kind: Service</code>) создаются только для сервисов, имеющих порты. Если сервис создан не будет redis-контейнер для разворачивания типа <code>Deployment</code> (<code>kind: Deployment</code>) не получит DNS-имя в рамках kubernetes-кластера и будет недоступен из других контейнеров (<code>web-контейнера</code>). <br />
<br />
* Добавлено описание переменной (<code>environment</code>) <code>REDIS_PORT: 6379</code>. При отсутствии этой переменной в web-контейнер передается некорректное значение этой переменной формата <code>http://&lt.IP>:6379</code>, что вызывает ошибку в <code>python-коде App/web.py</code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт развернутого стека в kubernetes-манифесты ===<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Формирование kubernetes-манифестов ====<br />
<br />
Для формирования запустите команду:<br />
<pre><br />
# podman kube generate pod_counter<br />
</pre><br />
<pre><br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
...<br />
name: podcounter<br />
spec:<br />
containers:<br />
...<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter_redis-pvc<br />
<br />
- image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
hostPort: 8080<br />
...<br />
volumes:<br />
- name: counter_redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter_redis<br />
</pre><br />
<br />
===== Запуск kubernetes-контейнеров =====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78106Podman-compose-to-kube2024-01-27T08:39:46Z<p>Kaf: </p>
<hr />
<div>== podman-compose-to-kube как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команд <br />
[https://github.com/containers/podman-compose podman-compose],<br />
[https://github.com/alt-cloud/podman-compose-to-kube podman-compose-to-kube].<br />
<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания <code>docker-compose</code> стеков необходимо установить пакеты <code>podman-compose</code>б <code>podman-compose-to-kube</code>. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем <code>root</code>. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
Каталог содержит в файле <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер и запускается томом <code>redis</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо утоснить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
Отличия от исходного файла:<br />
<br />
* Добавлено описание порта <code>6379</code> в сервис <code>redis</code>. Это связано с тем, что файлы kubernetes-сервисов (<code>kind: Service</code>) создаются только для сервисов, имеющих порты. Если сервис создан не будет redis-контейнер для разворачивания типа <code>Deployment</code> (<code>kind: Deployment</code>) не получит DNS-имя в рамках kubernetes-кластера и будет недоступен из других контейнеров (<code>web-контейнера</code>). <br />
<br />
* Добавлено описание переменной (<code>environment</code>) <code>REDIS_PORT: 6379</code>. При отсутствии этой переменной в web-контейнер передается некорректное значение этой переменной формата <code>http://&lt.IP>:6379</code>, что вызывает ошибку в <code>python-коде App/web.py</code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт развернутого стека в kubernetes-манифесты ===<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Формирование kubernetes-манифестов ====<br />
<br />
Для формирования запустите команду:<br />
<pre><br />
# podman kube generate pod_counter<br />
</pre><br />
<pre><br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
...<br />
name: podcounter<br />
spec:<br />
containers:<br />
...<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter_redis-pvc<br />
<br />
- image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
hostPort: 8080<br />
...<br />
volumes:<br />
- name: counter_redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter_redis<br />
</pre><br />
<br />
===== Запуск kubernetes-контейнеров =====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose/kubernetes&diff=78105Podman-compose/kubernetes2024-01-27T08:30:57Z<p>Kaf: Kaf переименовал страницу Podman-compose/kubernetes в Podman-compose-to-kubernetes: Написание скрипта podman-compose-to-kubernetes обеспечивающего описанный функционал</p>
<hr />
<div>#перенаправление [[Podman-compose-to-kubernetes]]</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=78104Podman-compose-to-kube2024-01-27T08:30:56Z<p>Kaf: Kaf переименовал страницу Podman-compose/kubernetes в Podman-compose-to-kubernetes: Написание скрипта podman-compose-to-kubernetes обеспечивающего описанный функционал</p>
<hr />
<div>== podman-compose как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является (полу)автоматическия генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команды <code>podman-compose</code>.<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания docker-compose стеков необходимо установить пакет <code>podman-compose</code>. <br />
Текущая (16.01.2024) версия <code>1.0.6</code> разворачивает контейнеры и тома с именами, которые после генерации на их основе YML-манифестов требуют дополнительной корректировки манифестов. В последующих версиях возможны изменения, которые обеспечат коррекное формирование имен контейнеров и томов. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем root. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
Каталог содержит в файле <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер и запускается томом <code>redis</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо утоснить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
Отличия от исходного файла:<br />
<br />
* Добавлено описание порта <code>6379</code> в сервис <code>redis</code>. Это связано с тем, что файлы kubernetes-сервисов (<code>kind: Service</code>) создаются только для сервисов, имеющих порты. Если сервис создан не будет redis-контейнер для разворачивания типа <code>Deployment</code> (<code>kind: Deployment</code>) не получит DNS-имя в рамках kubernetes-кластера и будет недоступен из других контейнеров (<code>web-контейнера</code>). <br />
<br />
* Добавлено описание переменной (<code>environment</code>) <code>REDIS_PORT: 6379</code>. При отсутствии этой переменной в web-контейнер передается некорректное значение этой переменной формата <code>http://&lt.IP>:6379</code>, что вызывает ошибку в <code>python-коде App/web.py</code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт развернутого стека в kubernetes-манифесты ===<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Формирование kubernetes-манифестов ====<br />
<br />
Для формирования запустите команду:<br />
<pre><br />
# podman kube generate pod_counter<br />
</pre><br />
<pre><br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
...<br />
name: podcounter<br />
spec:<br />
containers:<br />
...<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter_redis-pvc<br />
<br />
- image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
hostPort: 8080<br />
...<br />
volumes:<br />
- name: counter_redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter_redis<br />
</pre><br />
<br />
===== Запуск kubernetes-контейнеров =====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=77890Podman-compose-to-kube2024-01-24T18:31:21Z<p>Kaf: /* Запуск стека сервисов */</p>
<hr />
<div>== podman-compose как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является (полу)автоматическия генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команды <code>podman-compose</code>.<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания docker-compose стеков необходимо установить пакет <code>podman-compose</code>. <br />
Текущая (16.01.2024) версия <code>1.0.6</code> разворачивает контейнеры и тома с именами, которые после генерации на их основе YML-манифестов требуют дополнительной корректировки манифестов. В последующих версиях возможны изменения, которые обеспечат коррекное формирование имен контейнеров и томов. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем root. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
Каталог содержит в файле <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер и запускается томом <code>redis</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Перед запуском стека сервисов необходимо утоснить файл <code>docker-compose.yml</code>:<br />
<pre><br />
version: '3'<br />
volumes:<br />
redis:<br />
redis1:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
ports:<br />
- 6379<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
REDIS_PORT: 6379<br />
</pre><br />
Отличия от исходного файла:<br />
<br />
* Добавлено описание порта <code>6379</code> в сервис <code>redis</code>. Это связано с тем, что файлы kubernetes-сервисов (<code>kind: Service</code>) создаются только для сервисов, имеющих порты. Если сервис создан не будет redis-контейнер для разворачивания типа <code>Deployment</code> (<code>kind: Deployment</code>) не получит DNS-имя в рамках kubernetes-кластера и будет недоступен из других контейнеров (<code>web-контейнера</code>). <br />
<br />
* Добавлено описание переменной (<code>environment</code>) <code>REDIS_PORT: 6379</code>. При отсутствии этой переменной в web-контейнер передается некорректное значение этой переменной формата <code>http://&lt.IP>:6379</code>, что вызывает ошибку в <code>python-коде App/web.py</code>. <br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
''При использовании <code>podman-compose</code> версии <code>>= 1.0.7</code> параметр <code>--in-pod</code> необязателен.''<br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт развернутого стека в kubernetes-манифесты ===<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Формирование kubernetes-манифестов ====<br />
<br />
Для формирования запустите команду:<br />
<pre><br />
# podman kube generate pod_counter<br />
</pre><br />
<pre><br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
...<br />
name: podcounter<br />
spec:<br />
containers:<br />
...<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter_redis-pvc<br />
<br />
- image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
hostPort: 8080<br />
...<br />
volumes:<br />
- name: counter_redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter_redis<br />
</pre><br />
<br />
===== Запуск kubernetes-контейнеров =====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=77571Podman-compose-to-kube2024-01-19T14:06:24Z<p>Kaf: /* Запуск стека сервисов */</p>
<hr />
<div>== podman-compose как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является (полу)автоматическия генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команды <code>podman-compose</code>.<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания docker-compose стеков необходимо установить пакет <code>podman-compose</code>. <br />
Текущая (16.01.2024) версия <code>1.0.6</code> разворачивает контейнеры и тома с именами, которые после генерации на их основе YML-манифестов требуют дополнительной корректировки манифестов. В последующих версиях возможны изменения, которые обеспечат коррекное формирование имен контейнеров и томов. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем root. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
Каталог содержит в файле <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер и запускается томом <code>redis</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose --in-pod counter -p counter up -d<br />
</pre><br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт развернутого стека в kubernetes-манифесты ===<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Формирование kubernetes-манифестов ====<br />
<br />
Для формирования запустите команду:<br />
<pre><br />
# podman kube generate pod_counter<br />
</pre><br />
<pre><br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
...<br />
name: podcounter<br />
spec:<br />
containers:<br />
...<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter_redis-pvc<br />
<br />
- image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
hostPort: 8080<br />
...<br />
volumes:<br />
- name: counter_redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter_redis<br />
</pre><br />
<br />
===== Запуск kubernetes-контейнеров =====</div>Kafhttps://www.altlinux.org/index.php?title=Podman-compose-to-kube&diff=77561Podman-compose-to-kube2024-01-18T13:57:55Z<p>Kaf: /* Экспорт развернутого стека в kubernetes-манифесты */</p>
<hr />
<div>== podman-compose как средство миграция docker-compose решений в kubernetes ==<br />
<br />
Одной из основных проблем миграции <code>docker-compose</code> (<code>docker swarm</code>) решений в <code>kubernetes</code> является (полу)автоматическия генерация <code>kubernetes-манифестов</code> из <code>YAML-файлов описания стека сервисов</code>. <br />
Существует достаточно бедный набор инструментов, решающий данную проблему.<br />
Данный документ описывает решение данной проблемы путем использования команды <code>podman-compose</code>.<br />
В качестве примера разворачивания стека будет использоваться <code>docker-compose</code> стек [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python] проекта <code>podman-compose</code>.<br />
<br />
Будут рассмотрены вопросы разворачивания миграции как <code>rootfull</code> так и <code>rootless-решений</code>.<br />
<br />
=== Установка ПО, создание пользователей, разворачивание kubernetes ===<br />
<br />
Для разворачивания docker-compose стеков необходимо установить пакет <code>podman-compose</code>. <br />
Текущая (16.01.2024) версия <code>1.0.6</code> разворачивает контейнеры и тома с именами, которые после генерации на их основе YML-манифестов требуют дополнительной корректировки манифестов. В последующих версиях возможны изменения, которые обеспечат коррекное формирование имен контейнеров и томов. <br />
<br />
==== rootfull-окружение ====<br />
<br />
Разворачивание решений в <code>roofull</code> окружении производится под пользователем root. <br />
В создании других пользователей необходимости нет.<br />
Разворачивание <code>roofull-kubernetes</code> описано в документе [[Kubernetes]].<br />
<br />
==== rootless-решение ====<br />
Разворачивание <code>rooless-kubernetes</code> описано в документе [[Rootless kubernetes]].<br />
В процессе его разворачивания создается пользователь <code>u7s-admin</code>. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах <code>c10f.</code> это пользователи, входящие в группу <code>podman_dev</code>.<br />
Пользователь <code>u7s-admin</code> входит в эту группу.<br />
<br />
<br />
=== Разворачивание docker-compose стека в podman-compose ===<br />
<br />
==== Загрузка описания стека сервисов hello-python ====<br />
<br />
Скопируйте содержимое каталога [https://github.com/containers/podman-compose/tree/devel/examples/hello-python hello-python].<br />
<br />
Если у Вас установлен git это можно сделать командами:<br />
<pre><br />
# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git<br />
# cd podman-compose/<br />
# git sparse-checkout set --no-cone examples/hello-python<br />
# git checkout<br />
</pre><br />
После выполнения команд в каталоге <code>podman-compose/examples/hello-python</code> развернется содержание указанного выше каталога.<br />
<br />
==== Разворачивание стека сервисов ====<br />
<br />
===== Описание стека сервисов =====<br />
<br />
Перейдите в каталог <code>podman-compose/examples/hello-python</code>.<br />
Каталог содержит в файле <code>docker-compose.yml</code> описание стека сервисов:<br />
<pre><br />
---<br />
version: '3'<br />
volumes:<br />
redis:<br />
services:<br />
redis:<br />
read_only: true<br />
image: docker.io/redis:alpine<br />
command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]<br />
volumes:<br />
- redis:/data<br />
web:<br />
read_only: true<br />
build:<br />
context: .<br />
image: hello-py-aioweb<br />
ports:<br />
- 8080:8080<br />
environment:<br />
REDIS_HOST: redis<br />
</pre><br />
<br />
В сервисе <code>redis</code> запускается контейнер и запускается томом <code>redis</code>.<br />
<br />
В сервисе <code>web</code> собирается образ <code>hello-py-aioweb</code>, получающий имя <code>localhost/hello-py-aioweb</code> и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту <code>8080</code>.<br />
Образ <code>localhost/hello-py-aioweb</code> собирается на основе <code>Dockerfile</code>:<br />
<pre><br />
FROM python:3.9-alpine<br />
<br />
WORKDIR /usr/src/app<br />
<br />
COPY requirements.txt ./<br />
RUN pip install --no-cache-dir -r requirements.txt<br />
<br />
COPY . .<br />
<br />
CMD [ "python", "-m", "app.web" ]<br />
EXPOSE 8080<br />
</pre><br />
<br />
При запуске контейнера запускается python-скрипт <code>app/web.py</code>, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.<br />
<br />
===== Запуск стека сервисов =====<br />
<br />
Для запуска стека сервисов наберите команду:<br />
<pre><br />
podman-compose -p counter up -d<br />
</pre><br />
<br />
Параметр <code>-p</code> задает имя проекта - суффикс имени <code>POD</code>'а (<code>pod_counter</code>) и префикс имен контейнеров и томов.<br />
Если параметр <code>-p</code> отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае <code>hello-python</code>).<br />
<br />
В процессе работы <code>podman-compose</code> выводит список запускаемых команд:<br />
<pre><br />
...<br />
podman volume inspect counter_redis || podman volume create counter_redis<br />
...<br />
podman pod create --name=pod_counter --infra=false --share=<br />
...<br />
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...<br />
...<br />
</pre><br />
<br />
После запуска POD'а и контейнеров состояние можно посмотреть командами.<br />
- список запущенных POD'ов:<br />
<pre><br />
podman pod ls<br />
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS<br />
d37ba3addeb3 pod_counter Running 9 minutes ago 2<br />
</pre><br />
<br />
* Логи контейнеров POD'а:<br />
<pre><br />
podman pod logs pod_counter<br />
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
</pre><br />
<br />
* Список запущенных контейнеров:<br />
<pre><br />
podman ps<br />
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br />
...<br />
b5bdc8d1977f docker.io/library/redis:alpine redis-server --ap... 27 minutes ago Up 27 minutes counter_redis_1<br />
49f6f5141b24 localhost/hello-py-aioweb:latest python -m App.web 27 minutes ago Up 27 minutes 0.0.0.0:8080->8080/tcp counter_web_1<br />
...<br />
</pre><br />
<br />
* Логи контейнера базы данных redis<br />
<pre><br />
podman logs counter_redis_1<br />
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo<br />
...<br />
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp<br />
<br />
</pre> <br />
<br />
* Логи контейнера WEB-интерфейса web:<br />
<pre><br />
podman log counter_web_1<br />
</pre><br />
<br />
===== Проверка работы стека сервисов =====<br />
<br />
Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:<br />
<pre><br />
# curl localhost:8080/<br />
counter=1<br />
# curl localhost:8080/<br />
counter=2<br />
# curl localhost:8080/<br />
counter=3<br />
...<br />
</pre><br />
<br />
=== Экспорт развернутого стека в kubernetes-манифесты ===<br />
<br />
==== Копирование локальных образов в rootless окружении ====<br />
<br />
В rootless-окружении образы, созданные <code>podman-compose</code> хранятся в каталоге <code>/var/lib/u7s-admin/.local/share/containers/storage/</code>. Образы же для kubernetes хранятся в другом каталоге <code>/var/lib/u7s-admin/.local/share/usernetes/containers/storage/</code>. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске <code>POD</code>'а. Образы же, созданные локально, как в нашем случае образ <code>localhost/hello-py-aioweb</code> необходимо перенести в <code>container-storage</code> для kubernetes-образов командой <code>skopeo</code>:<br />
<pre># skopeo copy \<br />
containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ <br />
containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb<br />
</pre><br />
и изменить собственника перенесенного образа с <code>root</code> на <code>u7s-admin</code>:<br />
<pre># chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/</pre><br />
<br />
==== Формирование kubernetes-манифестов ====<br />
<br />
Для формирования запустите команду:<br />
<pre><br />
# podman kube generate pod_counter<br />
</pre><br />
<pre><br />
apiVersion: v1<br />
kind: Pod<br />
metadata:<br />
...<br />
name: podcounter<br />
spec:<br />
containers:<br />
...<br />
image: docker.io/library/redis:alpine<br />
name: counterredis1<br />
volumeMounts:<br />
- mountPath: /data<br />
name: counter_redis-pvc<br />
<br />
- image: localhost/hello-py-aioweb:latest<br />
name: counterweb1<br />
ports:<br />
- containerPort: 8080<br />
hostPort: 8080<br />
...<br />
volumes:<br />
- name: counter_redis-pvc<br />
persistentVolumeClaim:<br />
claimName: counter_redis<br />
</pre><br />
<br />
===== Запуск kubernetes-контейнеров =====</div>Kaf