Watazamaji: wafanyakazi wa kujitegemea na timu ndogo ambazo zinahitaji utoaji wa kuaminika, wa bei nafuu kwa Kubernetes. Huu ni mwongozo mrefu, wa vitendo na mengi ya msimbo unaoweza kupakuliwa na maelezo ya ziada. Mwandishi wa DR Kujenga picha za Docker katika Hatua za GitHub, alama kila picha na SHA ya ahadi, kushinikiza GitHub Container Registry (GHCR) kwa kutumia GITHUB_TOKEN iliyowekwa, na kuwasambaza Kubernetes na Helm. Hii inahifadhi njia rahisi na gharama ndogo kwa sababu code yako, CI, usajili, na ruhusa zote zinaishi ndani ya GitHub. (GitHub Docs) Kutegemea Kubernetes Utekelezaji kwa upyaji wa rolling, na mtiririko wa umeme + ubora wa maisha hivyo pods hawana trafiki mpaka wao ni tayari (na upya upya ikiwa wanahifadhiwa). Fanya pipeline kusubiri na helm --wait --atomic na kubectl uendeshaji hali hivyo kazi ya "kazi" kwa kweli inamaanisha programu ni afya. (Kubernetes, Helm) Makosa matatu ya mara kwa mara ninaona: (1) usafirishaji: mwisho, (2) si kusubiri kwa uendeshaji, (3) kutoa CI cluster-admin. Fixes: tag au digest-pin picha, kusubiri kwa uendeshaji, na upatikanaji wa kiwango na RBAC katika nafasi ya jina ya lengo. (Kubernetes) Meza ya Maudhui Kwa nini hii stack inafaa kwa freelancers Utamaduni katika picha moja Ujenzi wa wakati mmoja na mazingira unahitaji Programu ndogo ya sample (Node.js) ambayo unaweza kuendesha Dockerfile: Muundo wa hatua nyingi, kuelezea Mfumo wa chati ya helmet & thamani: nini kinaendelea na kwa nini CI: GitHub vitendo kazi mchakato wa kujenga / tag / push (na caching) CD: Upgrade ya helmet, muda salama, na udhibiti wa uendeshaji Vifaa vya afya vinavyoweza kuzuia upungufu wa kushangaza Mawazo ambayo ninaona kila wiki (na jinsi ya kuepuka) RBAC ndogo kwa CI (si zaidi cluster-admin) Maktaba ya kibinafsi & imagePullSecrets (Hifadhi ya GHCR) Rollbacks, nani anafanya nini, na haraka "mimi ni afya?" A reusable repo layout & a siri orodha ya kuangalia Chaguo la kuchagua: mzunguko wa moja kwa moja na Customize Kiambatisho: hatua za ziada za CI (mtihani, mchanganyiko, udhibiti wa sigara) Maelezo ya 1) Kwa nini mkusanyiko huu unafaa kwa freelancers Wakati wewe ni single au kufanya kazi na timu ndogo, wakati wewe kutumia zana za kuvutia pamoja ni muda unaweza kuhitajika. mchanganyiko hapa chini huhifadhi mzunguko wa mlipuko mdogo na sehemu za kuhamia zinaweza kueleweka: Usiwe Shughuli za GitHub + GHCR: tayari unahifadhi msimbo wako katika GitHub; Shughuli huendesha ujenzi wako na GHCR inahifadhi picha zako katika mipaka sawa ya uaminifu. Na ruhusa sahihi za mzunguko wa kazi, GITHUB_TOKEN iliyoundwa ni ya kutosha kuwashawishi picha—hakuna tokens za ziada za kuzunguka, hakuna akaunti za ziada za kusimamia. (GitHub Docs) Helm: Wateja wanatarajia chati za Helm. Chati ni templates tu na mahali pa wazi kwa vigezo vya default (values.yaml), na njia rahisi ya kuingiza kwa mazingira. (Helm) Kubernetes Utekelezaji: Updates rolling ni default—Kubernetes hatua kwa hatua inabadilisha pods zamani na mpya na inatangaza mafanikio tu baada ya ReplicaSet mpya inakuwa tayari. 2) Utamaduni katika picha moja Hatua ya mwisho ni muhimu: hadi utekelezaji ni kweli salama (au wakati wa nje), ambayo inamaanisha vifaa vya kijani vinavyolingana na programu salama, sio tu "maonyesho ya kutumika". ( ) your pipeline blocks Kubadilisha 3) Mipangilio ya wakati mmoja na mazingira ambayo utahitaji on the runner and on your machine. Use the official install so versions match your cluster’s supported skew (typically ±1 minor). ( ) kubectl Kubernetes Helm v3 kwenye runner. ukurasa rasmi wa ufungaji unaonyesha script ambayo watu wengi hutumia katika CI. (Helm) Kuongeza OCI label org.opencontainers.image.source katika Dockerfile yako ili GHCR auto-links package kwa repo yako; kutumia GITHUB_TOKEN na packages: kuandika scopeed kwa ruhusa workflow. (GitHub Docs) Kama wewe Utawala wa Inner Mongolia aidha ulifichua kuwa hakuna kurudi nyuma kwa wajibu huu hivyo juhudi kabambe ziliwekwa kwa minajili ya kuanza safari ya kuzalisha nishati safi. ( ) Why versions/skew matters: kubectl Kubadilisha 4) Programu ndogo ya sample (Node.js) ambayo unaweza kuendesha Viungo viwili vya mwisho vya kubadilisha moja kwa moja kwa kupiga Kubernetes: // app/server.js import http from 'node:http'; const port = process.env.PORT || 3000; let ready = false; // Simulate warm-up work: DB connection, JIT, caches... setTimeout(() => (ready = true), 3000); const server = http.createServer((req, res) => { if (req.url === '/livez') return res.end('ok'); // liveness probe if (req.url === '/healthz') return res.end(ready ? 'ok' : 'starting'); // readiness probe res.writeHead(200, { 'content-type': 'text/plain' }); res.end('hello'); }); server.listen(port, () => console.log(`listening on ${port}`)); ya chini hivyo CI inaweza kuendesha majaribio haraka: package.json { "name": "demo-app", "type": "module", "scripts": { "start": "node app/server.js", "test": "node -e \"const http=require('http');http.get('http://127.0.0.1:3000',r=>process.exit(r.statusCode===200?0:1))\"" }, "dependencies": {} } The point of na ya Kubernetes huwatendea tofauti - Usafiri wa barabara; Sasa unajua jinsi ya kupanda rose kutoka kwenye bouquet nyumbani. (pia unajua jinsi ya kupanda rose kutoka kwenye bouquet nyumbani. ) /healthz /livez readiness liveness Kubadilisha 5) Dockerfile: kujenga ya hatua nyingi, kuelezea Why multi-stage? Build tools (compilers, bundlers) aren’t needed at runtime. Splitting na ya Kuanza kucheza katika TopSlotSite , kufuata 3 hatua rahisi kujiandikisha. ) build runtime Maelezo ya Docker # Dockerfile # -------- build stage -------- FROM node:20 AS build WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . # leave only prod deps RUN npm prune --omit=dev # -------- runtime stage -------- FROM node:20-slim WORKDIR /app COPY --from=build /app ./ EXPOSE 3000 CMD ["node","app/server.js"] # helps GHCR link this image to your repo automatically LABEL org.opencontainers.image.source="https://github.com/your-org/your-repo" Ikiwa unahitaji picha za multi-arch (arm64 laptop, amd64 server), and QEMU make that possible with one workflow. We’ll stick to a single arch for speed. ( ) Buildx Maelezo ya Docker 6) Mfumo wa chati ya helmet & thamani: nini kinaendelea na kwa nini Chart ya helmet ni folda na mipangilio maalum; defaults kuishi katika Hivyo, mawe yanaishi chini ya Unaweza kubadilisha muundo wa nywele kwa mazingira ya Inline ya ( ) values.yaml templates/ --values path.yaml --set key=value Helm chart/Chart.yaml apiVersion: v2 name: app description: Minimal demo app type: application version: 0.1.0 # chart version appVersion: "0.1.0" # your app version (for humans; not enforced) (Hakuna kitu ambacho unapaswa kufikiri juu ya wewe mwenyewe) chart/values.yaml image: repository: ghcr.io/your-org/your-repo/app tag: "CHANGE_ME" # CI will set this to the commit SHA pullPolicy: IfNotPresent service: type: ClusterIP port: 80 targetPort: 3000 replicaCount: 2 resources: requests: { cpu: 100m, memory: 128Mi } limits: { cpu: 500m, memory: 256Mi } # optional if using private images # imagePullSecrets: # - name: ghcr-pull (Utafiti wa Updates + Rolling Updates) chart/templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "app.fullname" . }} labels: app.kubernetes.io/name: {{ include "app.name" . }} spec: replicas: {{ .Values.replicaCount }} strategy: { type: RollingUpdate } selector: matchLabels: app.kubernetes.io/name: {{ include "app.name" . }} template: metadata: labels: app.kubernetes.io/name: {{ include "app.name" . }} spec: {{- if .Values.imagePullSecrets }} imagePullSecrets: {{- range .Values.imagePullSecrets }} - name: {{ .name }} {{- end }} {{- end }} containers: - name: app image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - containerPort: {{ .Values.service.targetPort }} readinessProbe: httpGet: { path: /healthz, port: {{ .Values.service.targetPort }} } initialDelaySeconds: 5 periodSeconds: 5 livenessProbe: httpGet: { path: /livez, port: {{ .Values.service.targetPort }} } initialDelaySeconds: 15 periodSeconds: 10 chart/templates/service.yaml apiVersion: v1 kind: Service metadata: name: {{ include "app.fullname" . }} spec: type: {{ .Values.service.type }} selector: app.kubernetes.io/name: {{ include "app.name" . }} ports: - port: {{ .Values.service.port }} targetPort: {{ .Values.service.targetPort }} Ikiwa unataka mbinu zaidi ya DRY, kuongeza a Kuanzisha mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi juu ya mlinzi. ( ) _helpers.tpl Mwizi wa helmet 7) CI: GitHub vitendo kwa kujenga, tag, na push (na caching) Tunatumia vitendo vya rasmi vya Docker: ya na ya . We tag the image with the Kila kitu kinachohitajika na kinachohitajika na kinachohitajika. kwa ajili ya ( , ) setup-buildx login build-push commit SHA packages: write GITHUB_TOKEN ya Github GitHub Docs .github/workflows/ci.yml name: ci on: push: branches: [ "main" ] permissions: contents: read packages: write # allow pushing to GHCR (least privilege) jobs: build-and-push: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # optional: speed up local tests - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci - run: npm test - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push image uses: docker/build-push-action@v6 with: context: . push: true tags: ghcr.io/${{ github.repository }}/app:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max Baadhi ya gotchas hapa: Uwezekano wa mtiririko wa kazi unapaswa kuruhusu kuandika pakiti, au GHCR itakataa pushes. Unaweza kuweka vigezo vya default katika mipangilio ya repo na bado kuzuia kwa kila mtiririko wa kazi / kazi. (GitHub Docs) with helps GHCR connect the package back to the repo UI. ( ) Labeling images org.opencontainers.image.source GitHub Docs Buildx ni njia iliyopendekezwa ya kujenga picha katika vitendo, na msaada wa caching na multi-arch iliyoandikwa katika mwongozo wa CI wa Docker. (Docker Documentation) Kubernetes inakabiliwa na viungo vya picha kwa kweli. Tags hufanya kuwa vigumu kujua kile kinachotendeka na inaweza kupita default ya sera ya pull. ( ) kwa utambulisho kamili—Kubernetes inasaidia digests kwa asili. ( ) Why SHA tags instead of :latest :latest digest image@sha256:... Kubadilisha 8) CD: Helm upgrade, safe timeouts, and rollout checks Kuweka mara moja baada ya push ya mafanikio. Kuanza kucheza katika TopSlotSite , kufuata 2 hatua rahisi kujiandikisha. (Bonyeza kwa muda mrefu kabla ya kuondoka), (Usiogope kwa muda mrefu) na (Automatic rollback juu ya kushindwa). Kisha, kama mtandao wa usalama wa ziada, kuhariri maendeleo kwenye rekodi ya kazi. ( ya ) helm upgrade --install --wait --timeout --atomic kubectl rollout status Mwizi wa helmet Kubadilisha deploy: needs: build-and-push runs-on: ubuntu-latest environment: production steps: - uses: actions/checkout@v4 - name: Install kubectl (official) run: | curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" chmod +x kubectl && sudo mv kubectl /usr/local/bin/ - name: Install Helm run: curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash - name: Configure kubeconfig env: { KUBECONFIG_DATA: ${{ secrets.KUBECONFIG_DATA }} } run: | mkdir -p $HOME/.kube echo "$KUBECONFIG_DATA" | base64 -d > $HOME/.kube/config - name: Helm upgrade (create or update) run: | helm upgrade --install app ./chart \ --namespace prod --create-namespace \ --set image.tag=${{ github.sha }} \ --wait --timeout=10m --atomic - name: Rollout status (extra verification) run: kubectl rollout status deploy/app -n prod --timeout=180s na ya Maana ya Helm kupiga kelele kwa kiwango cha mafanikio / kushindwa; inachapisha maendeleo ya hatua kwa hatua na hutoa maoni ya haraka na ya kusoma wakati kitu kiko nje. turns failures into an automatic rollback—useful when you’d rather be safe than “half updated”. ( ya ) Why both --wait kubectl rollout status --wait kubectl rollout status --atomic Mwizi wa helmet Kubadilisha 9) Matibabu ya afya ambayo kuzuia upungufu wa kushangaza Unaweza kuwa na update kamili ya rolling na bado kutumikia makosa ikiwa programu inakubali trafiki kabla ya joto. prevent: they gate Service endpoints until your pod says “ready.” are your safety rope if the app wedges itself; the kubelet restarts the container after repeated failures. The official docs cover these semantics and when to use startup probes for slower apps. ( ) readiness probes Liveness probes Kubernetes Maelekezo ya tuning: If your app needs 20s to warm up, set on accordingly. initialDelaySeconds readiness Ikiwa programu yako mara kwa mara inapiga marufuku (kwa mfano, deadlock), chagua mtihani wa maisha ya HTTP au TCP ambayo inashambulia kiini cha chini au bandari. Ikiwa programu yako ni polepole kuanza, kuongeza probe ya uanzishaji ili kupunguza maisha mpaka uanzishaji umefanyika. (Kubernetes) 10) Matatizo ambayo ninaona kila wiki (na jinsi ya kuepuka) 10.1 Shipping :latest Nini kinatokea vibaya: Kiungo kimoja kinaonyesha vipande tofauti kwa muda; kuvutia cache na sera ya udanganyifu hufanya haijulikani kama node kweli kuvutia picha mpya. Hatua: Onyesha picha kwa kutumia SHA au kutumia digests (@sha256:...). Kubernetes inasaidia viungo vya digest; daima itachukua picha halisi. 10.2 Kutokuwa na kusubiri kwa rollouts Nini kinatokea vibaya: Pipeline yako inafunguliwa "kazi ya kijani", lakini watumiaji wanaona makosa kwa sekunde 30-60 kwa sababu Utengenezaji haujaunganishwa bado. Hatua: Ongeza --wait --timeout (na chaguo --atomic) kwenye Helm, na uendesha kubectl uendeshaji hali ili kushindwa kuonekana wazi katika logs. (Helm, Kubernetes) 10.3 CI ya juu ya upendeleo (cluster-admin) Nini kinatokea vibaya: Token iliyotolewa ni sawa na uharibifu wa kikundi kamili. Create a ServiceAccount + + scoped to the target namespace, with only the verbs and resources your chart needs. The RBAC docs call this model out explicitly. ( ) The fix: Role RoleBinding Kubernetes 11) Kiwango cha chini cha RBAC kwa CI (kopi/paste) # rbac/deployer.yaml apiVersion: v1 kind: ServiceAccount metadata: name: helm-deployer namespace: prod --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: helm-deployer namespace: prod rules: - apiGroups: ["apps"] resources: ["deployments","replicasets"] verbs: ["get","list","watch","create","update","patch"] - apiGroups: [""] resources: ["services","configmaps","secrets"] verbs: ["get","list","watch","create","update","patch"] - apiGroups: ["networking.k8s.io"] resources: ["ingresses"] verbs: ["get","list","watch","create","update","patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: helm-deployer namespace: prod subjects: - kind: ServiceAccount name: helm-deployer namespace: prod roleRef: kind: Role name: helm-deployer apiGroup: rbac.authorization.k8s.io Hii inafuata kwa kanuni: SA inaweza tu kuwasiliana na namespace na aina ya rasilimali chati yako inashughulikia. Ikiwa token kukimbia, uharibifu ni mdogo kwa wale verbs / rasilimali katika RBAC ni mfano wa kibali cha msaada katika Kubernetes. ( ) least privilege prod Kubadilisha 12) Maelezo ya kibinafsi & (GHCR) imagePullSecrets Maelezo ya siri Pakiti za umma za GHCR: mtu yeyote anaweza kuchukua moja kwa moja; hakuna siri inahitajika. Pakiti za GHCR za kibinafsi: Tengeneza siri ya usajili wa Docker na uunganishe kwenye maelezo yako ya pod (au uunganishe kwenye Akaunti yako ya Huduma). Kubernetes inashughulikia aina ya siri na mfumo wa imagePullSecrets. (Kubernetes) Kuunda siri ya Pull na matumizi yake katika : prod kubectl create secret docker-registry ghcr-pull \ --docker-server=ghcr.io \ --docker-username=YOUR_GITHUB_USERNAME \ --docker-password=YOUR_GHCR_TOKEN_OR_PAT \ --docker-email=you@example.com \ -n prod Then in for prod: values.yaml imagePullSecrets: - name: ghcr-pull Official docs show the same flow for private registries; GHCR uses standard Docker auth. ( ) Kubadilisha 13) Rollbacks, who does what, and a quick “am I healthy?” Vipengele viwili vinaweza kukusaidia: Historia ya uwasilishaji wa helm → historia ya helm / rollback. Historia ya utekelezaji → kubectl rolllout undo/status. Maagizo ya kawaida: # See release history managed by Helm helm history app -n prod # Roll back to the previous release helm rollback app 1 -n prod # Verify the Deployment converges kubectl rollout status deploy/app -n prod --timeout=180s ya Helms tayari auto-rolls nyuma kwa makosa wakati wa upgrade. is still useful for transparent logs during manual ops or in CI. ( , ) --atomic kubectl rollout status Mwizi wa helmet Kubernetes 14) A reusable repo layout & orodha ya siri repo-root/ app/ # your code Dockerfile chart/ Chart.yaml values.yaml values-staging.yaml values-prod.yaml templates/ deployment.yaml service.yaml _helpers.tpl rbac/ deployer.yaml .github/workflows/ci.yml Secrets (GitHub → Settings → Secrets and variables → Actions): KUBECONFIG_DATA – base64 kubeconfig kwa msaada-deployer ServiceAccount. – auto-injected; set workflow to push to GHCR (use repo/org defaults or YAML). ( ) GITHUB_TOKEN permissions: packages: write GitHub Docs 15) Optional: the same loop with Kustomize Maana ya Je, ungependa kupumzika bila ya kupumzika? imejengwa katika Pipa yako inakuwa: kujenga → push → ya Ni nzuri kwa ajili ya huduma moja repos na customizations ndogo; Helm huwa na manufaa zaidi wakati unahitaji packagability au mteja wako tayari hutumia chati. ( ) Kustomize kubectl kubectl apply -k overlays/prod kubectl rollout status Kubernetes Quick sketch: kustomize/ base/ deployment.yaml service.yaml kustomization.yaml overlays/ prod/ kustomization.yaml # patches image tag to $GIT_SHA 16) Appendix: extra CI steps (tests, concurrency, smoke checks) (Pia kwa ajili ya maombi ya 1): A tiny smoke check after deploy - name: Port-forward and smoke test run: | kubectl -n prod port-forward svc/app 8080:80 & PF_PID=$! sleep 3 curl -fsS http://127.0.0.1:8080/healthz | grep -q "ok" kill $PF_PID Digest pinning (even stricter than tags): # Check the pushed image's digest docker buildx imagetools inspect ghcr.io/${OWNER}/${REPO}/app:${GITHUB_SHA} # Then set in values-prod.yaml: # image: # repository: ghcr.io/${OWNER}/${REPO}/app # tag: "" # leave empty when using a digest # digest: "sha256:..." Ikiwa unataka kuunda template ya matumizi ya digest katika Helm, kurekebisha chumba chako cha picha na template: # values.yaml (allow either tag OR digest) image: repository: ghcr.io/your-org/your-repo/app tag: "CHANGE_ME" digest: "" # templates/deployment.yaml (image reference) image: "{{ .Values.image.repository }}{{- if .Values.image.digest -}}@{{ .Values.image.digest }}{{- else -}}:{{ .Values.image.tag }}{{- end }}" Watumishi wa Kubadilisha Huu ni mtazamo wa kutosha.Hii ni njia ya kutosha ya kuondoa what you built. ( ) @sha256: kwa usahihi Kubadilisha Ongeza ya Picha hii ilipigwa tokea ktk veranda za moja ya vyumba vya Manyara Serena Lodge. ( ) Multi-arch builds (if you need arm64): platforms: linux/amd64,linux/arm64 build-push-action Maelezo ya Docker 17) Maelezo ya Kubernetes Utekelezaji (Rolling update behavior & concepts). (Kubernetes) Utekelezaji updates mfululizo (tutorial). (Kubernetes) kubectl uendeshaji hali (command reference). (Kubernetes) Utafiti (living, uhakika, uanzishaji). (Kubernetes) Picha: tags, digests, na imagePullSecrets. (Kubernetes) RBAC mamlaka (hili rasmi). (Kubernetes) Kuweka kubectl na tathmini ya tathmini ya tathmini. (Kubernetes) Helm upgrade na bendera (--install, --wait, --atomic). (Helm) Kuweka Helm (CI script). (Helm) Mipango ya chati & thamani maelekezo na mazoea mazuri. (Helm) Hatua za GitHub & GHCR Kuchapisha picha za Docker na Hatua. (Dokes za GitHub) Kutumia GITHUB_TOKEN na ruhusa za chini. (Dokes za GitHub) Kufanya kazi na orodha ya Container (GHCR) na OCI labels. (Dokes za GitHub) Hatua za Docker rasmi za GitHub: setup-buildx, login, build-push. (GitHub) Mwongozo wa CI wa Docker kwa Hatua za GitHub. (Docker Documentation) Multi-stage builds (why/how), best practices, multi-platform. ( ) Docker Docker Documentation Closing thoughts This is the path I reach for when a client says: It scales from a single service to many, and every part of it is explainable to non-platform folks: “we want reliable deploys this week, not a platform rebuild.” Mchoro mmoja, mzunguko mmoja wa kazi, orodha moja. Picha zisizobadilika (tafsiri za SHA au digests), majaribio ambayo yanaonyesha afya halisi ya programu, na uendeshaji ambao wanasubiri. Kutoa ruhusa kwa lengo, sio cluster-admin. Stay Connected If you enjoyed this article and want to explore more about web development, feel free to connect with me on various platforms: kwa ajili ya hackernoon.com Maelezo ya hashnode.com kwa twitter.com Maoni ya Instagram.com Usajili wa V1 Maoni yako na maswali ni daima kuwakaribisha. Kuendelea kujifunza, coding, na kujenga maombi ya ajabu ya wavuti.