piątek, 1 czerwca 2018

Instalacja i konfiguracja GlusterFS

Dane wejściowe:
  • 4 serwery Ubuntu 16.04.4 LTS,
  • 2048 GB RAM-u każdy z serwerów.
Zaczynamy od wpisania do "/etc/hosts" każdego z serwerów informacji o nazwach i adresach IP wszystkich maszyn z naszej sieci GlusterFS. Dla przykładu:
192.168.33.30   kubernetes-master1192.168.33.31   kubernetes-worker1192.168.33.32   kubernetes-worker2192.168.33.33   kubernetes-worker3
Dodajemy wszędzie informację o repozytorium:
sudo add-apt-repository ppa:gluster/glusterfs-3.11
Aktualizujemy bazę:
sudo apt-get update
Instalujemy oprogramowanie:
sudo apt-get install glusterfs-server
Ze względów bezpieczeństwa wyłączamy aktualizację pakietów GlusterFS:
sudo apt-mark hold glusterfs*
Startujemy oprogramowanie i dodajemy konfigurację, która uruchamia usługę podczas startu systemu:
sudo systemctl start glusterd
sudo systemctl enable glusterd
Na pierwszy z serwerów sprawdzamy po kolei czy w sieci są dostępne wszystkie węzły:
vagrant@kubernetes-master1:~$ sudo gluster peer probe kubernetes-worker1
peer probe: success.
vagrant@kubernetes-master1:~$ sudo gluster peer probe kubernetes-worker2
peer probe: success.
vagrant@kubernetes-master1:~$ sudo gluster peer probe kubernetes-worker3
peer probe: success.
Tworzymy wolumen:
vagrant@kubernetes-master1:~$ sudo gluster volume create gvol0 replica 4 kubernetes-master1:/data kubernetes-worker1:/data kubernetes-worker2:/data kubernetes-worker3:/data force
volume create: gvol0: success: please start the volume to access data
Opcja "force" utworzy nam katalog na serwerach jeżeli takowy nie istnieje.

Aktualnie każdy komputer ma dostęp bez żadnych restrykcji do naszego wolumenu z danymi więc dopuszczamy połączenie montujące tylko z:
vagrant@kubernetes-master1:~$ sudo gluster volume set gvol0 auth.allow 127.0.0.1
volume set: success
Startujemy wolumen i wyświetlamy informacje na jego temat:
vagrant@kubernetes-master1:~$ sudo gluster volume start gvol0
volume start: gvol0: success
vagrant@kubernetes-master1:~$ sudo gluster volume status
Status of volume: gvol0
Gluster process                             TCP Port  RDMA Port  Online  Pid
------------------------------------------------------------------------------
Brick kubernetes-master1:/data              49152     0          Y       17935
Brick kubernetes-worker1:/data              49152     0          Y       8542
Brick kubernetes-worker2:/data              49152     0          Y       8411
Brick kubernetes-worker3:/data              49152     0          Y       8472
Self-heal Daemon on localhost               N/A       N/A        Y       17955
Self-heal Daemon on kubernetes-worker3      N/A       N/A        Y       8492
Self-heal Daemon on kubernetes-worker2      N/A       N/A        Y       8431
Self-heal Daemon on kubernetes-worker1      N/A       N/A        Y       8562
Task Status of Volume gvol0
------------------------------------------------------------------------------
There are no active volume tasks
vagrant@kubernetes-master1:~$ sudo gluster volume info
Volume Name: gvol0
Type: Replicate
Volume ID: ab3ed814-191e-4288-ae1e-341f9a1bca40
Status: Started
Snapshot Count: 0
Number of Bricks: 1 x 4 = 4
Transport-type: tcp
Bricks:
Brick1: kubernetes-master1:/data
Brick2: kubernetes-worker1:/data
Brick3: kubernetes-worker2:/data
Brick4: kubernetes-worker3:/data
Options Reconfigured:
auth.allow: 127.0.0.1
transport.address-family: inet
nfs.disable: on
Na każdym serwerze tworzymy katalog "/mnt/gluster".

Na każdej z maszyn montujemy wolumen (w zależności od tego na której jesteś to zmień nazwę) np.:
sudo mount -t glusterfs kubernetes-master1:/gvol0 /mnt/gluster
Teraz możemy manipulować zawartością tylko w "/mnt/gluster", ale pojawi sie ona również w katalogu "/data".

Każdej maszynie dodajemy wpis w "/etc/fstab" (w zależności od tego na której jesteś to zmień nazwę) np.:
kubernetes-master1:/gvol0 /mnt/gluster glusterfs defaults,_netdev 0 0

środa, 18 kwietnia 2018

OpenShift i prywatne Docker Registry

Czasami zdarza się, że nasze obrazy kontenerów dockerowych przetrzymujemy w prywatnym rejestrze. Wymaga to wykonania przez nas dodatkowych operacji, które umożliwią nam pobranie danego obrazu.

Na początek logujemy się do OpenShifta tokenem
oc login https://127.0.0.1:8443 --token=viNUjgpTzIReqx-FHRU-c3XEEFojhio4tEQeJ5_3TgE
lub przez login i hasło
oc login -u admin -p admin
Kolejnym krokiem jest zalogowanie się do naszego Registry aby wygenerować plik z danymi uwierzytelniającymi:
docker login adres_serwera
Plik znajduje się pod adresem "/home/katalog_domowy/.docker/config.json". Teraz musimy wygenerować zasób przechowujący dane uwierzytelniające:
oc create secret generic mojeregistry --from-file=.dockerconfigjson=/katalog_domowy/.docker/config.json --type=kubernetes.io/dockerconfigjson
Dalej:
oc secrets link default mojeregistry --for=pull
oc secrets link builder mojeregistry --for=pull
oc secrets link deployer mojeregistry --for=pull
W tym miejscu mała dygresja, a mianowicie OpenShift domyślnie odpala aplikację w kontenerze jako użytkownik o losowym identyfikatorze celem bezpieczeństwa. Prawa są ograniczone i może się tak zdarzyć, że program nie będzie mógł się odpalić. Jeżeli nie można zmienić tego w OpenShifcie to konieczna jest modyfikacja Dockerfile'a. Aby dokonać zmiany w OpenShifcie najpierw wprowadzić
oc edit scc restricted
która otworzy nam plik jednego z Security Context Constraints.

Odnajdujemy linie
runAsUser:
   type: MustRunAsRange
i zamieniamy na
runAsUser:
   type: RunAsAny
Powyższe operacje umożliwią nam pobranie obrazu z prywatnego Registry.

środa, 4 kwietnia 2018

Klaster Kubernetes

Dane wejściowe:
  • 3 serwery wirtualne z Ubuntu 16.04 LTS,
  • 2048 MB RAM-u,
  • 2 procesory,
  • wszystkie komendy uruchamiane jako root.
Na początek na każdym z serwerów wyłączamy partycję wymiany aby kubelet (agent uruchomiony na każdym węźle) działał poprawnie
  1. wydajemy komendę "free -h" i sprawdzamy czy w linijce oznaczonej jako "Swap:" i kolumnie "total" mamy wartość różną od zera,
  2. edytujemy plik "/etc/fstab" i komentujemy linijkę odpowiadającą za montowanie partycji wymiany,
  3. restartujemy serwer. 
Kolejnym punktem jest instalacja Dockera na każdym z węzłów. Na początek aktualizujemy dane o pakietach:
apt-get update
Instalujemy potrzebne oprogramowanie:
apt-get install -y apt-transport-https ca-certificates curl software-properties-common
Dodajemy klucz danego repozytorium jako zaufany:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
Wprowadzamy informacje o repozytorium:
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
Instalacja Dockera:
apt-get update && apt-get install docker-ce
Przejdźmy teraz do instalacji oprogramowania orkiestracyjnego. Na początek wprowadzamy klucz do repozytoriów:
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
Teraz informacja o repozytorium:
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
Uaktualniamy informacje o pakietach i instalujemy oprogramowanie:
apt-get update
apt-get install -y kubelet kubeadm kubectl
Na głównym węźle naszego klastra dokonujemy edycji pliku "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf" i dopisujemy w nim linijkę z odpowiednim adresem IP:
Environment="KUBELET_EXTRA_ARGS=--cgroup-driver=cgroupfs --node-ip=adres_IP_wezla"
Na węzłach wtórnych postępujemy podobnie tylko linijka z odpowiednim adresem IP ma wyglądać następująco:
Environment="KUBELET_EXTRA_ARGS=--node-ip=adres_IP_wezla" 
Przeładowujemy usługę:
systemctl daemon-reload
systemctl restart kubelet
Jesteśmy gotowi do inicjalizacja klastra, ale wcześniej musimy ustalić jakiej wtyczki do sieci strąków (z ang. "pods") użyjemy. Lista jest dostępna na tej stronie.

Dla tego przykładu wybrano Calico.

Usługa kubelet może jeszcze nie działać, ale nie przejmujemy się tym gdyż teraz przechodzimy do inicjalizacji klastra (na głównym węźle):
root@kubernetes-server0:~# kubeadm init --pod-network-cidr=192.168.0.0/16
[init] Using Kubernetes version: v1.10.0
[init] Using Authorization modes: [Node RBAC]
[preflight] Running pre-flight checks.
    [WARNING SystemVerification]: docker version is greater than the most recently validated version. Docker version: 18.03.0-ce. Max validated version: 17.03
    [WARNING FileExisting-crictl]: crictl not found in system path
Suggestion: go get github.com/kubernetes-incubator/cri-tools/cmd/crictl
[certificates] Generated ca certificate and key.
[certificates] Generated apiserver certificate and key.
[certificates] apiserver serving cert is signed for DNS names [kubernetes-server0 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.121.122]
[certificates] Generated apiserver-kubelet-client certificate and key.
[certificates] Generated etcd/ca certificate and key.
[certificates] Generated etcd/server certificate and key.
[certificates] etcd/server serving cert is signed for DNS names [localhost] and IPs [127.0.0.1]
[certificates] Generated etcd/peer certificate and key.
[certificates] etcd/peer serving cert is signed for DNS names [kubernetes-server0] and IPs [192.168.121.122]
[certificates] Generated etcd/healthcheck-client certificate and key.
[certificates] Generated apiserver-etcd-client certificate and key.
[certificates] Generated sa key and public key.
[certificates] Generated front-proxy-ca certificate and key.
[certificates] Generated front-proxy-client certificate and key.
[certificates] Valid certificates and keys now exist in "/etc/kubernetes/pki"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/admin.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/controller-manager.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/scheduler.conf"
[controlplane] Wrote Static Pod manifest for component kube-apiserver to "/etc/kubernetes/manifests/kube-apiserver.yaml"
[controlplane] Wrote Static Pod manifest for component kube-controller-manager to "/etc/kubernetes/manifests/kube-controller-manager.yaml"
[controlplane] Wrote Static Pod manifest for component kube-scheduler to "/etc/kubernetes/manifests/kube-scheduler.yaml"
[etcd] Wrote Static Pod manifest for a local etcd instance to "/etc/kubernetes/manifests/etcd.yaml"
[init] Waiting for the kubelet to boot up the control plane as Static Pods from directory "/etc/kubernetes/manifests".
[init] This might take a minute or longer if the control plane images have to be pulled.
[apiclient] All control plane components are healthy after 52.501186 seconds
[uploadconfig] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[markmaster] Will mark node kubernetes-server0 as master by adding a label and a taint
[markmaster] Master kubernetes-server0 tainted and labelled with key/value: node-role.kubernetes.io/master=""
[bootstraptoken] Using token: 5x7pop.hvg1betp3zxyl0qh
[bootstraptoken] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: kube-dns
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join 192.168.121.122:6443 --token 5x7pop.hvg1betp3zxyl0qh --discovery-token-ca-cert-hash sha256:bd6bc43d0ef54551c0ccfbc48451ae09d3cc46cc8813a5e827e947c40cb59fc9
Jeżeli chcemy używać kubectl jako zwykły użytkownik to (jako dany użytkownik):
mkdir -p $HOME/.kube
Jako root:
cp -i /etc/kubernetes/admin.conf KATALOG_DOMOWY_UŻYTKOWNIKA/.kube/config
chown UŻYTKOWNIK:GRUPA KATALOG_DOMOWY_UŻYTKOWNIKA/.kube/config
Jeżeli chcemy sterować klastrem z poziomu roota to odpalamy
export KUBECONFIG=/etc/kubernetes/admin.conf
Kopiujemy komendę pozwalającą na dołączanie węzłów do klastra. W naszym przypadku jest to
kubeadm join 192.168.121.122:6443 --token 5x7pop.hvg1betp3zxyl0qh --discovery-token-ca-cert-hash sha256:bd6bc43d0ef54551c0ccfbc48451ae09d3cc46cc8813a5e827e947c40cb59fc9
W przypadku jeżeli coś pójdzie nie tak i będziemy chcieli znów zainicjalizować klaster to musimy najpierw pobrać nazwę głównego węzła:
kubectl get nodes
Następnie usuwamy węzeł:
kubectl drain NAZWA_WĘZŁA --delete-local-data --force --ignore-daemonsets
kubectl delete node NAZWA_WĘZŁA
Resetujemy:
kubeadm reset
Jeżeli mamy zainicjalizowany klaster to instalujemy sieć strąków:
kubectl apply -f https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml
kubectl apply -f https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml 
Poniższa komenda pokaże nam status konkretnego strąka:
kubectl get pods --all-namespaces
Jeżeli status strąka z przedtrostkiem "kube-dns" będzie ciągle w stanie "Pending" zamiast "Running" musimy sprawdzić co jest powodem przestoju, a robimy to przykładową dla naszego przypadku komendą:
kubectl --namespace=kube-system describe pod kube-dns-86f4d74b45-4rsst
Ze względów bezpieczeństwa domyślnie nie można wdrażać kontenerów na głównym węźle. Jeżeli jednak chcesz to robić to uruchom poniższą komendę:
kubectl taint nodes --all node-role.kubernetes.io/master-
Na każdym z węzłów pracujących uruchamiamy poniższą komendę, która doda je do klastra:
kubeadm join 192.168.121.122:6443 --token 5x7pop.hvg1betp3zxyl0qh --discovery-token-ca-cert-hash sha256:bd6bc43d0ef54551c0ccfbc48451ae09d3cc46cc8813a5e827e947c40cb59fc9
Na głównym węźle sprawdzamy status klastra:
kubectl get nodes
Jeżeli chcemy sterować z innych węzłów musimy skopiować z głównego plik "/etc/kubernetes/admin.conf" i na jednym z hostów uruchomić kubectl jako np.
kubectl --kubeconfig ./admin.conf get nodes

piątek, 30 marca 2018

Apache ZooKeeper oraz Apache Kafka w OpenShifcie

Jeżeli nie posiadamy wykupionej lub darmowej usługi OpenShift (Starter lub Pro) to możemy postawić sobie lokalnie własny klaster OpenShift (opis powstał w oparciu o system Ubuntu).

Mając już zainstalowanego Dockera (warto dodać użytkownika do grupy "docker" aby nie trzeba było wykonywać komend dockerowych z prawami roota) sprawdzamy za pomocą komendy (jako root)
sysctl net.ipv4.ip_forward
czy wartością zwracaną jest 1.

Następnie konfigurujemy Dockera aby można było korzystać z niezaufanego Registry lokalnego OpenShifta poprzez edycję pliku "/etc/docker/daemon.json" i dodanie i (lub) aktualizację jak następuje:
{
   "insecure-registries": [
      "172.30.0.0/16"
   ]
}
Po wszystkim wywołujemy (jako root)
systemctl daemon-reload
i
systemctl restart docker
Pobieramy archiwum z narzędziem do zarządzania OpenShiftem:
wget https://github.com/openshift/origin/releases/download/v3.9.0/openshift-origin-client-tools-v3.9.0-191fece-linux-64bit.tar.gz
Rozpakowujemy:
tar -zxvf openshift-origin-client-tools-v3.9.0-191fece-linux-64bit.tar.gz
Kopiujemy (jako root):
cp ./openshift-origin-client-tools-v3.9.0-alpha.3-78ddc10-linux-64bit/oc /usr/bin/
Ustalamy prawa (jako root):
chmod 755 /usr/bin/oc
Stawianie i wyłączanie klastra odbywa się odpowiednio poprzez komendy
oc cluster up
i
oc cluster down
Po postawieniu klastra dostajemy zwrotkę mówiącą pod jakim adresem mamy interfejs oraz dane logowania:
Starting OpenShift using registry.access.redhat.com/openshift3/ose:v3.7.23 ...
OpenShift server started.

The server is accessible via web console at:
    https://127.0.0.1:8443

You are logged in as:
    User:     developer
    Password: <any value>

To login as administrator:
    oc login -u system:admin
Aby mieć możliwość pełnej administracji (m.in. dostęp do wszystkich projektów i zarządzanie różnego rodzaju politykami) musimy (jako root) najpierw zalogować się do OpenShifta:
oc login -u admin -p admin
Następnie (jako root):
oc adm policy add-cluster-role-to-user cluster-admin system --config=/var/lib/origin/openshift.local.config/master/admin.kubeconfig
Wychodzimy z konta roota i wchodzimy na stronę z OpenShiftem. W moim przypadku jest to "https://127.0.0.1:8443/". Logujemy się jako "system" i hasło "admin".

Zalogować się do naszej platformy możemy również za pomocą tokena. Na stronie OpenShifta wprowadzamy login i hasło, a następnie po pomyślnym uwierzytelnieniu prawym górnym rogu klikamy na ikonę użytkownika i wybieramy "Copy Login Command" i zawartość schowka wklejamy do terminala aby się zalogować jak np.:
oc login https://127.0.0.1:8443 --token=6TmPUCncEyzqXglh3goioYJSBasTGb7thS-vyaXIoVU
Możemy się również zalogować wpisując
oc login -u system -p admin
Sprawdźmy jakie mamy projekty:
oc get project
Następnie używamy konkretnego projektu:
oc project nazwa_projektu
lub tworzymy nowy projekt:
oc new-project kafka
Warto zaznaczyć aby na każdym etapie tworzenia poszczególnych zasobów w OpenShifcie sprawdzać czy nie wystąpiły jakieś błędy.

Nazwa projektu musi się zgadzać z opcją "namespace" w plikach konfiguracyjnych jeżeli takowa występuje.

Przechowywanie danych w klastrze jest obsługiwane przez tworzenie ze źródeł obiektów PersistentVolume. Dostęp do nich możesz uzyskać rozszcząc sobie prawa do zasobu poprzez wysłanie żądania (ze specjalnymi atrybutami jak np. rozmiar magazynu z danymi) przy użyciu obiektu PersistentVolumeClaim. Pomiędzymi tymi dwoma obiektami istnieje proces, który dopasowuje żądanie z dostępnym wolumenem i wiąże je ze sobą.

PersistentVolume reprezentuje część istniejącego sieciowego miejsca do przechowywania danych w klastrze, który został postawiony przez administratora. Z kolei PersistentVolumeClaim reprezentuje żądanie użytkownika o przydzielenia miejsca. Innymi słowy tak jak strąk (z ang. "pod") konsumuje zasoby węzła tak PersistenceVolumeClaim konsumuje zasoby PersistentVolume.

Tworzymy plik "kafka_pvc.yml" z definicją obiektów PersistentVolumeClaim dla Kafki:
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: datadir-kafka-0
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: datadir-kafka-1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: datadir-kafka-2
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: datadir-kafka-3
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: datadir-kafka-4
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
Wprowadzamy komendę, która utworzy nam magazyny danych:
oc create -f kafka_pvc.yml
Następnie plik "zookeeper_pvc.yml" dla ZooKeepera:
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: datadir-zoo-0
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: datadir-zoo-1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: datadir-zoo-2
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
Odpowiednio podajemy komendę:
oc create -f zookeeper_pvc.yml
Aby sprawdzić jakie mamy dostępne wolumeny wpisujemy:
oc get pvc
Przechodzimy do wdrażania naszych usług, które w Kubernetesie obsługiwane są jako wewnętrzne równoważenie obciążenia (z ang. "internal load balancer"). Można zdefiniować zestaw zreplikowanych strąków ("pods") i wtedy pośredniczyć w przekazywaniu do nich połączeń. Usługi mają przypisany adres IP oraz port, za pomocą którego przekazuje się połączenia do odpowiedniego strąka.

Rekomendowana maksymalna liczba strąków na węzeł OpenShifta to 110.

Tworzymy plik "zookeeper_services.yml" o zawartości:
apiVersion: v1
kind: Service
metadata:
  name: zookeeper
spec:
  ports:
  - port: 2181
    name: client
  selector:
    app: zk
---
apiVersion: v1
kind: Service
metadata:
  annotations:
    service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
  name: zk
  labels:
    app: zk
spec:
  ports:
  - port: 2888
    name: peer
  - port: 3888
    name: leader-election
  clusterIP: None
  selector:
    app: zk
Odpalamy tworzenie usług jako:
oc create -f zookeeper_services.yml
StatefulSet jest obiektem używanym do zarządzania aplikacjami stanowymi; wdrażaniem i skalowaniem zestawem strąków.

Kolejnym punktem jest wpisanie do pliku "zookeeper_cluster.yml" następujących danych:
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: zoo
spec:
  serviceName: "zk"
  replicas: 3
  template:
    metadata:
      labels:
        app: zk
      annotations:
        pod.alpha.kubernetes.io/initialized: "true"
        pod.alpha.kubernetes.io/init-containers: '[
            {
                "name": "install",
                "image": "gcr.io/google_containers/zookeeper-install:0.1",
                "imagePullPolicy": "Always",
                "args": ["--version=3.5.2-alpha", "--install-into=/opt", "--work-dir=/work-dir"],
                "volumeMounts": [
                    {
                        "name": "opt",
                        "mountPath": "/opt/"
                    },
                    {
                        "name": "workdir",
                        "mountPath": "/work-dir"
                    }
                ]
            },
            {
                "name": "bootstrap",
                "image": "java:openjdk-8-jre",
                "command": ["/work-dir/peer-finder"],
                "args": ["-on-start=\"/work-dir/on-start.sh\"", "-service=zk"],
                "env": [
                  {
                      "name": "POD_NAMESPACE",
                      "valueFrom": {
                          "fieldRef": {
                              "apiVersion": "v1",
                              "fieldPath": "metadata.namespace"
                          }
                      }
                   }
                ],
                "volumeMounts": [
                    {
                        "name": "opt",
                        "mountPath": "/opt/"
                    },
                    {
                        "name": "workdir",
                        "mountPath": "/work-dir"
                    },
                    {
                        "name": "datadir",
                        "mountPath": "/tmp/zookeeper"
                    }
                ]
            }
        ]'
    spec:
      containers:
      - name: zk
        image: java:openjdk-8-jre
        ports:
        - containerPort: 2181
          name: client
        - containerPort: 2888
          name: peer
        - containerPort: 3888
          name: leader-election
        command:
        - /opt/zookeeper/bin/zkServer.sh
        args:
        - start-foreground
        readinessProbe:
          exec:
            command:
            - sh
            - -c
            - "/opt/zookeeper/bin/zkCli.sh ls /"
          initialDelaySeconds: 15
          timeoutSeconds: 5
        volumeMounts:
        - name: datadir
          mountPath: /tmp/zookeeper
        - name: opt
          mountPath: /opt/
      volumes:
      - name: opt
        emptyDir: {}
      - name: workdir
        emptyDir: {}
  volumeClaimTemplates:
  - metadata:
      name: datadir
      annotations:
        volume.alpha.kubernetes.io/storage-class: anything
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 20Gi
Odpalamy jako aby stworzyć StatefulSet "zoo":
oc create -f zookeeper_cluster.yml
Przechodzimy do wdrażania Kafki. Zaczniemy od wprowadzenia poniższych danych do pliku "kafka_broker_service.yml":
---
apiVersion: v1
kind: Service
metadata:
  annotations:
    service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
  name: broker
spec:
  ports:
  - port: 9092
  clusterIP: None
  selector:
    app: kafka
Odpalamy jako:
oc create -f kafka_broker_service.yml
Każdy z brokerów, do których możemy się podłączyć aby wysyłać wiadomości będzie dostępny pod nazwą hosta według wzoru "kafka-NUMER_WĘZŁA.broker.kafka.svc.cluster.local".

Kolejną rzeczą jest następna usługa, której definicję wrzucamy do pliku "kafka_service.yml":
---
apiVersion: v1
kind: Service
metadata:
  name: kafka
spec:
  ports:
  - port: 9092
  selector:
    app: kafka
 
Wprowadzamy komendę:
oc create -f kafka_service.yml
Na koniec definicja klastra Kafki w pliku "kafka_cluster.yml":
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: kafka
spec:
  serviceName: "broker"
  replicas: 3
  template:
    metadata:
      labels:
        app: kafka
      annotations:
        pod.alpha.kubernetes.io/initialized: "true"
        pod.alpha.kubernetes.io/init-containers: '[
        ]'
    spec:
      containers:
      - name: broker
        image: solsson/kafka:0.10.0.1
        ports:
        - containerPort: 9092
        command:
        - sh
        - -c
        - "./bin/kafka-server-start.sh config/server.properties --override broker.id=$(hostname | awk -F'-' '{print $2}')"
        volumeMounts:
        - name: datadir
          mountPath: /opt/kafka/data
  volumeClaimTemplates:
  - metadata:
      name: datadir
      annotations:
        volume.alpha.kubernetes.io/storage-class: anything
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 100Mi
Ostatecznie wprowadzamy:
oc create -f kafka_cluster.yml
Teraz musimy sobie przetestować nasze wdrożenia. W tym celu tworzymy nowy strąk ("pod") definiując go w pliku "kafka_client_pod.yml":
apiVersion: v1
kind: Pod
metadata:
  name: testclient
spec:
  containers:
  - name: kafka
    image: solsson/kafka:0.10.0.1
    command:
      - sh
      - -c
      - "exec tail -f /dev/null"
I odpalamy:
oc create -f kafka_client_pod.yml
Na dwóch oddzielnych terminalach odpalamy komendy umożliwiające nad zalogowanie się do osobnych sesji w kontenerach do testów:
oc rsh testclient
W pierwszym terminalu tworzymy temat w ZooKeeperze:
./bin/kafka-topics.sh --zookeeper zookeeper:2181 --create --partitions 1 --replication-factor 3 --topic test
W tym samym terminalu podłączamy się do ZooKeepera aby konsumować wiadomości:
./bin/kafka-console-consumer.sh --zookeeper zookeeper:2181 --topic test --from-beginning
Na drugim terminalu wpisujemy:
./bin/kafka-console-producer.sh --broker-list kafka-0.broker.kafka.svc.cluster.local:9092,kafka-1.broker.kafka.svc.cluster.local:9092,kafka-2.broker.kafka.svc.cluster.local:9092 --topic test
i wprowadzamy jakikolwiek ciąg znaków, który powinien pojawić się nam w pierwszym terminalu.

Aby usunąć wszystkie strąki ("pody"), usługi i klastry to wpisujemy:
oc delete all --all
Dodatkowo usuwamy wszystkie wolumeny:
oc delete pvc --all

sobota, 10 marca 2018

Cassandra - kopie zapasowe

Tworzenie kopii zapasowych w Cassandrze polega na wykonywaniu migawek wszystkich plików z danymi (pliki SSTable). Możesz zrobić migawkę wszystkich przestrzeni kluczy (z ang. "keyspaces") lub pojedyńczej.

Migawkę tworzymy komendą
nodetool snapshot
lub
 nodetool -h adres_serwera -p numer_portu snapshot
Powyższe komendy utworzą migawki wszystkich przestrzeni kluczy. Jeżeli chcemy zrobić migawkę konkretnej przestrzeni to wpisujemy:
nodetool snapshot nazwa_przestrzeni_kluczy
Po wykonaniu komendy podany zostanie nam numer migawki.

Dane kopii (pliki o rozszerzeniu "db") znajdziemy w "katalog_Cassandry/data/nazwa_przestrzeni_kluczy/nazwa_tabeli/snapshots/numer_migawki".

Poprzednie kopie nie są usuwane więc warto przedtem wywołać
nodetool snapshot clearsnapshot
lub
nodetool snapshot clearsnapshot nazwa_przestrzeni_kluczy
Przywracanie danych wymaga wszystkich plików migawek dla tabeli, a jeżeli używane są inkrementowane kopie zapasowe to wszystkie pliki inkrementowanych kopii zapasowych utworzone po tym jak wykonana została migawka.

Generalnie przed przystąpieniem do przywracania danch powinno się usunąć wszystkie dane z tabeli i ustawić odpowiednią konsystencję:
truncate nazwa_przestrzeni_klucz.nazwa_tabeli;
consistency all;
Cassandra może przywracać dane kiedy istnieje schemat tabel. Jeżeli tego nie zrobiłeś to możesz albo:
- przywrócić dane z migawki,
- ponownie stworzyć schemat
lub:
- stworzyć ponownie schemat,
- przywrócić migawkę,
- uruchomić "nodetool refresh".

Aby przywrócić dane do konkretnej tabeli należy:
1) wyłączyć Cassandrę,
2) usunąć wszystkie pliki z "katalog_Cassandry/commitlog",
3) usunąć wszystkie pliki o rozszerzeniu "db" z "katalog_Cassandry/data/nazwa_przestrzeni_kluczy/nazwa_tabeli",
4) skopiować zawartość katalogu "katalog_Cassandry/data/nazwa_przestrzeni_kluczy/nazwa_tabeli/snapshots/numer_migawki" do "katalog_Cassandry/data/nazwa_przestrzeni_kluczy/nazwa_tabeli" i zmienić właściciela oraz grupę na odpowiednie dla serwera Cassandry (zwykle jest to "cassandra" i "cassandra"),
5) włączyć Cassandrę.

W celu automatyzacji powyższych zadań stworzone zostało narzędzie Carmela.

Logi w RAM-ie

Aby przedłużyć żywotność karty używanej jako dysk w Raspberry Pi warto przechowywać logi w pamięci RAM. Aby to zrobić do "/etc/fstab" dodajemy następujący wpis:
tmpfs /var/log tmpfs defaults,noatime,nosuid,mode=0755,size=100m 0 0

sobota, 3 marca 2018

MongoDB - użytkownicy

Po zainstalowaniu serwera MongoDB warto zabezpieczyć instancję poprzez dodanie użytkowników administrujących serwerem i włączyć uwierzytelnianie.

Poniższa procedura dodaje użytkownika, który może administrować innymi użytkownikami w bazie (upewnij się wcześniej czy już takowy nie istnieje):
use admin;
db.createUser( { user: "siteUserAdmin", pwd: "haslo", roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] } );
Kolejny użytkownik może np. włączać serwer i tworzyć inne bazy:
use admin;db.createUser( { user: "siteRootAdmin", pwd: "haslo", roles: [ { role:  "root", db: "admin" } ] } );
Następnie do pliku konfiguracyjnego serwera dodajemy:
 security:
authorization: enabled
Aby uwierzytelnianie zadziałało musimy zrestartować serwer.

Teraz możemy zalogować się na dwa sposoby:
- "mongo --username uzytkownik --password haslo --authenticationDatabase nazwa_bazy",
- wywolujemy w powloce komende "mongo", a następnie:
 use nazwa_bazy;
db.auth("uzytkownik", "haslo");
Przystępując do procesu tworzenia nowych użytkowników należy najpierw stworzyć użytkownika administrującego systemem, a potem z jego poziomu tworzyć innych użytkowników.

Rola stworzona w bazie danych "admin" może zawierać przywileje, które mają zastosowanie do bazy danych "admin", innych baz lub zasobów klastra.