Minikube 사용방법

|

Minikube 사용방법

명령어 설명
minikube start minikube 가상머신 실행
minikube stop minikube 가상머신 정지
minikube delete 가상머신 제거
minikube status 상태 확인
minikube ip IP 주소 확인
minikube ssh ssh 접속
minikube addons list 애드온 목록 조회
minikube dashboard 브라우저로 minikube 대시보드 접속

대시보드 url 확인

$ minikube dashboard  --url

🤔  Verifying dashboard health ...
🚀  Launching proxy ...
🤔  Verifying proxy health ...
http://127.0.0.1:53502/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/

MAC OS에 minikube 설치하는 방법

|

MAC OS에 minikube 설치

다음과 같이 brew를 이용해서 설치하면 됩니다.

$ brew install minikube

Running `brew update --auto-update`...
==> Downloading https://ghcr.io/v2/homebrew/portable-ruby/portable-ruby/blobs/sha256:905b0c3896164ae8067a22fff2fd0b80b16d3c8bb72441403eedf69da71ec717
################################################################################################################# 100.0%
==> Pouring portable-ruby-2.6.10_1.arm64_big_sur.bottle.tar.gz
==> Homebrew collects anonymous analytics.
Read the analytics documentation (and how to opt-out) here:
  https://docs.brew.sh/Analytics
No analytics have been recorded yet (nor will be during this `brew` run).

Installing from the API is now the default behaviour!
You can save space and time by running:
  brew untap homebrew/core
  brew untap homebrew/cask
==> Downloading https://formulae.brew.sh/api/formula.jws.json
################################################################################################################# 100.0%
==> Downloading https://formulae.brew.sh/api/cask.jws.json
################################################################################################################# 100.0%
==> Fetching dependencies for minikube: kubernetes-cli
==> Fetching kubernetes-cli

...


==> `brew cleanup` has not been run in the last 30 days, running now...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
Removing: /Users/snowdeer/Library/Caches/Homebrew/nvm--0.39.3... (47.2KB)
Removing: /Users/snowdeer/Library/Caches/Homebrew/nvm_bottle_manifest--0.39.3... (1.7KB)
Removing: /Users/snowdeer/Library/Logs/Homebrew/nvm... (64B)
==> Caveats
==> minikube
zsh completions have been installed to:
  /opt/homebrew/share/zsh/site-functions

만약 brew를 사용하지 않으면 다음 명령어를 이용해서 minikube를 설치할 수 있습니다.

$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-arm64
$ sudo install minikube-darwin-arm64 /usr/local/bin/minikube

설치 확인

$ minikube version

minikube version: v1.31.1
commit: fd3f3801765d093a485d255043149f92ec0a695f

minikube 실행

$ minikube start

😄  minikube v1.31.1 on Darwin 12.4 (arm64)
✨  Automatically selected the docker driver
📌  Using Docker Desktop driver with root privileges
👍  Starting control plane node minikube in cluster minikube
🚜  Pulling base image ...
💾  Downloading Kubernetes v1.27.3 preload ...
    > preloaded-images-k8s-v18-v1...:  327.72 MiB / 327.72 MiB  100.00% 1.65 Mi
    > gcr.io/k8s-minikube/kicbase...:  404.50 MiB / 404.50 MiB  100.00% 1.76 Mi
🔥  Creating docker container (CPUs=2, Memory=1988MB) ...
🐳  Preparing Kubernetes v1.27.3 on Docker 24.0.4 ...
    ▪ Generating certificates and keys ...
    ▪ Booting up control plane ...
    ▪ Configuring RBAC rules ...
🔗  Configuring bridge CNI (Container Networking Interface) ...
    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🔎  Verifying Kubernetes components...
🌟  Enabled addons: storage-provisioner, default-storageclass
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

kubectl 명령어로 pods 정보 확인

$ kubectl get po -A

NAMESPACE              NAME                                         READY   STATUS    RESTARTS        AGE
kube-system            coredns-5d78c9869d-bmsck                     1/1     Running   0               3m28s
kube-system            etcd-minikube                                1/1     Running   0               3m41s
kube-system            kube-apiserver-minikube                      1/1     Running   0               3m41s
kube-system            kube-controller-manager-minikube             1/1     Running   0               3m41s
kube-system            kube-proxy-qcp9m                             1/1     Running   0               3m28s
kube-system            kube-scheduler-minikube                      1/1     Running   0               3m41s
kube-system            storage-provisioner                          1/1     Running   1 (2m57s ago)   3m39s
kubernetes-dashboard   dashboard-metrics-scraper-5dd9cbfd69-nvbmj   1/1     Running   0               113s
kubernetes-dashboard   kubernetes-dashboard-5c5cfc8747-8g7mn        1/1     Running   0               113s

Reference

  • https://minikube.sigs.k8s.io/docs/start/

Pixi.js Viewport 예제코드

|

Pixi.js Viewport 예제코드

PixiView.vue

<template>
  <div id="container">
    <canvas id="pixi-canvas"></canvas>
  </div>
</template>

<script>
import "./js/pixi-app";
import { PixiApp } from "./js/pixi-app";
export default {
  mounted() {
    this.createPixiApp();
  },
  methods: {
    createPixiApp() {
      const app = new PixiApp(800, 600);
      const canvas = document.getElementById("pixi-canvas");
      canvas.appendChild(app.view);
      console.log(`window size(${window.innerWidth}, ${window.innerHeight})`);
      console.log(`app size(${app.view.width}, ${app.view.height})`);
    },
  },
};
</script>

<style>
#container {
  display: block;
  width: 100vw;
  height: 100vh;
  background: whitesmoke;
}
#pixi-canvas {
  display: block;
  margin: 5%;
}
</style>

js/pixi-app.js

import * as PIXI from "pixi.js-legacy";
import { Viewport } from "pixi-viewport";

class PixiApp extends PIXI.Application {
  constructor(width, height) {
    super({
      width: width,
      height: height,
      backgroundColor: 0xffebee,
      antialias: true,
    });

    const screenWidth = 800;
    const screenHeight = 600;
    const worldWidth = screenWidth * 2;
    const worldHeight = screenHeight * 2;

    const viewport = new Viewport({
      screenWidth: screenWidth,
      screenHeight: screenHeight,
      worldWidth: worldWidth,
      worldHeight: worldHeight,

      interaction: this.renderer.plugins.interaction,
    });

    this.stage.addChild(viewport);

    const grid = this.#createGrid();
    viewport.addChild(grid);

    const boundary = this.#createBoundary(worldWidth, worldHeight);
    viewport.addChild(boundary);

    this.#registerViewportEventHandler(viewport);

    console.log(viewport);
    console.log(`Screen width: ${viewport.screenWidth}`);
    console.log(`World width: ${viewport.worldWidth}`); // World height, in pixels
    console.log(
      `Screen width in World pixels width(${viewport.screenWidthInWorldPixels})`
    ); // Get how many world pixels fit in screen's width
    console.log(`Screen World width(${viewport.screenWorldWidth})`); // World width in screen coordinates
    console.log(`World Screen width(${viewport.worldScreenWidth})`); // Screen width in world coordinates
    console.log(`Viewport Corner`, viewport.corner); // Screen width in world coordinates
    console.log(`Viewport Center`, viewport.center); // Screen width in world coordinates
  }

  #registerViewportEventHandler(viewport) {
    const screenWidth = 800;
    const screenHeight = 600;

    viewport
      .drag()
      .pinch()
      .wheel()
      .decelerate()
      .clamp({
        direction: "all",
        underflow: "center",
      })
      .clampZoom({
        minWidth: screenWidth,
        minHeight: screenHeight,
        maxWidth: screenWidth * 3,
        maxHeight: screenHeight * 3,
      })
      .clampZoom({
        minScale: 0.5,
        maxScale: 2,
      })
      .fit();

    viewport.on("pointerup", (e) => {
      const x = e.data.global.x;
      const y = e.data.global.y;
      console.log(`point UP (${x}, ${y})`); // Viewport 좌표
      console.log(`toScreen: `, viewport.toScreen(x, y));
      console.log(`toWorld: `, viewport.toWorld(x, y));
      console.log(`visibleBounds()`, viewport.getVisibleBounds());
    });
  }

  #createBoundary(width, height) {
    const container = new PIXI.Container();
    const boundary = new PIXI.Graphics();
    boundary.lineStyle(8, 0xff0000);
    boundary.drawRect(0, 0, width, height);
    container.addChild(boundary);

    return container;
  }

  #createGrid() {
    const container = new PIXI.Container();
    const origin = this.#createCrossPoint({
      x: 0,
      y: 0,
      color: 0xff0000,
      size: 50,
      fontsize: 14,
    });
    container.addChild(origin);

    for (let row = 0; row <= 20; row++) {
      for (let col = 0; col <= 20; col++) {
        if (row == 0 && col == 0) continue;

        const p = this.#createCrossPoint({
          x: col * 100,
          y: row * 100,
        });
        container.addChild(p);
      }
    }

    return container;
  }

  #createCrossPoint({
    x,
    y,
    color = 0x000000,
    size = 5,
    thickness = 1,
    fontsize = 8,
  }) {
    const container = new PIXI.Container();

    const grid = new PIXI.Graphics();
    grid.lineStyle(thickness, color);
    grid.moveTo(x - size, y);
    grid.lineTo(x + size, y);
    grid.moveTo(x, y - size);
    grid.lineTo(x, y + size);
    container.addChild(grid);

    const text = new PIXI.Text(`(${x},${y})`, {
      fontFamily: "Arial",
      fontSize: fontsize,
      fill: color,
    });
    text.position.set(x + 3, y + 3);
    container.addChild(text);

    return container;
  }
}

export { PixiApp };

실행화면

화면의 각 위치를 터치해보고, 화면을 panning으로 이동 후 각 위치를 터치, Zoom 등으로 화면을 확대/축소하면서 각 위치를 터치하면서 좌표계를 확인해볼 수 있습니다.

image

각 이벤트를 거쳐 전달되는 좌표값은 Viewport 상의 좌표이며, 이를 World 좌표로 변환하면 각 오브젝트가 그려진 Canvas 상의 좌표라고 볼 수 있습니다.

Pixi.js Viewport 상에서의 Event

|

Pixi.js Viewport 상에서의 Event

pixi-viewport라는 유틸리티를 이용해서 구현한 예제입니다.

package.json

아래와 같이 모듈을 추가해주면 됩니다.

...

"dependencies": {
  "core-js": "^3.8.3",
  "pixi-viewport": "^4.34.4",
  "pixi.js-legacy": "^6.3.0",
  "vue": "^3.2.13"
},

...

PixiView.vue

<template>
  <div id="container">
    <div id="pixi-canvas"></div>
  </div>
</template>

<script>
import "./js/pixi-app";
import { PixiApp } from "./js/pixi-app";
export default {
  mounted() {
    this.createPixiApp();
  },
  methods: {
    createPixiApp() {
      const app = new PixiApp(800, 800);
      const canvas = document.getElementById("pixi-canvas");
      canvas.appendChild(app.view);
      console.log(`window size(${window.innerWidth}, ${window.innerHeight})`);
      console.log(`app size(${app.view.width}, ${app.view.height})`);
    },
  },
};
</script>

<style>
#container {
  display: block;
  width: 100vw;
  height: 100vh;
  background: whitesmoke;
}
#pixi-canvas {
  display: block;
  margin: 20px;
}
</style>

pixp-app.js

import * as PIXI from "pixi.js-legacy";
import { Viewport } from "pixi-viewport";

class PixiApp extends PIXI.Application {
  constructor(width, height) {
    super({
      width: width,
      height: height,
      backgroundColor: 0xffebee,
      antialias: true,
    });

    const viewport = new Viewport({
      screenWidth: width,
      screenHeight: height,
      worldWidth: width * 2,
      worldHeight: height * 2,

      interaction: this.renderer.plugins.interaction, // the interaction module is important for wheel to work properly when renderer.view is placed or scaled
    });

    this.stage.addChild(viewport);

    const rect = this.#createRect(100, 100);
    viewport.addChild(rect);
    rect.interactive = true;
    rect.on("pointerdown", (e) => {
      console.log(`rect DOWN (${e.data.global.x}, ${e.data.global.y})`); // Viewport 좌표
    });

    const grid = this.#createGrid();
    viewport.addChild(grid);

    viewport.drag().pinch().wheel().decelerate();

    viewport.on("pointerdown", (e) => {
      console.log(`point DOWN (${e.data.global.x}, ${e.data.global.y})`); // Viewport 좌표
    });
  }

  #createRect(centerX, centerY, width = 100, height = 100) {
    const rect = new PIXI.Graphics();
    rect.beginFill(0xff8080);
    rect.drawRect(0, 0, width, height);
    rect.pivot.set(width / 2, height / 2);
    rect.position.set(centerX, centerY);
    rect.endFill();

    return rect;
  }

  #createGrid() {
    const grid = new PIXI.Graphics();
    grid.lineStyle(2, 0xf06292);
    grid.moveTo(-2000, 0);
    grid.lineTo(2000, 0);
    grid.moveTo(0, -2000);
    grid.lineTo(0, 2000);
    grid.lineStyle(1, 0x000000);
    for (let i = 1; i < 5; i++) {
      grid.moveTo(i * 400, -2000);
      grid.lineTo(i * 400, 2000);
      grid.moveTo(-2000, i * 400);
      grid.lineTo(2000, i * 400);
    }

    return grid;
  }
}

export { PixiApp };

실행화면

image

Pixi.js Event 등록하기

|

Pixi 캔버스 상의 오브젝트에 이벤트 등록하기

아래와 같은 코드로 작성하면 다음과 같은 이벤트들을 사용할 수 있습니다.

  • pointerdown
  • pointermove
  • pointerup
  • click
  • pointerover
  • pointerout

참고로, 이벤트들은 Pixi.js에서 등록된 이벤트가 아니라 HTML5에 정의되어 있는 이벤트들입니다.

<template>
  <div id="container">
    <canvas id="pixi-canvas"></canvas>
  </div>
</template>

<script>
import * as PIXI from "pixi.js";
import { onMounted } from "@vue/runtime-core";

export default {
  setup() {
    onMounted(() => {
      const app = createPixiApp();

      createInteractionExample(app);
    });

    const createPixiApp = () => {
      var canvas = document.getElementById("pixi-canvas");

      const app = new PIXI.Application({
        view: canvas,
      });

      return app;
    };

    const createInteractionExample = (app) => {
      const container = new PIXI.Container();
      const c1 = new PIXI.Graphics();
      c1.lineStyle(0);
      c1.beginFill(0x008080);
      c1.drawCircle(400, 100, 50);
      c1.endFill();
      container.addChild(c1);

      app.stage.addChild(container);

      c1.interactive = true;
      c1.on("pointerdown", (e) => {
        console.log("pointerdown");
        console.log(e);
      });
      // c1.on("pointermove", (e) => {
      //   console.log("pointermove");
      //   console.log(e);
      // });
      c1.on("pointerup", (e) => {
        console.log("pointerup");
        console.log(e);
      });
      c1.on("click", (e) => {
        console.log("click");
        console.log(e);
      });
      c1.on("pointerover", (e) => {
        console.log("pointerover");
        console.log(e);
      });
      c1.on("pointerout", (e) => {
        console.log("pointerout");
        console.log(e);
      });
    };
  },
};
</script>

<style>
#container {
  display: block;
  background: white;
  padding: 20px;
}

#pixi-canvas {
  width: 600px;
  height: 600px;
}
</style>

이벤트

개인적으로 Pixi이 Event들은 만족스럽지 않습니다. 제 프로젝트의 문제인지 일시적 버그인지 모르겠지만, 위의 예제에서 오브젝트에 설정한 pointermove 이벤트가 오브젝트가 아닌 캔버스 전체에 적용되는 버그도 있으며, pointdown, pointup, click 이벤트들이 일반적인 형태로 동작하지 않는 것 같았습니다. 보통 pointdown 후 좌표를 크게 이동한 후 pointup을 할 경우 click 이벤트는 호출되지 않는데, Pxii에서는 항상 호출되네요.

그리고 아래와 같이 로그에서도 모든 이벤트의 typemousemove로 전달되어 오는 등 아쉬운 점이 많네요.

image