[WSL & K8S] 워드프레스 블로그 구축 하기

개발 블로그 운영이 필요하게 되어, 클라우드와 홈서버 등을 검토 하는 도 중, 클라우드의 VPS 를 검토 하였으나, 기능과 비용 면에서 어려움이 있어, 홈서버를 구성하고자 하였습니다.

01. Ubuntu 가 아닌 WSL 선택의 이유

홈서버를 구성할 때, 지금은 도커와 같은 컨테이너 시스템을 선호하고 있습니다. 기존 환경보다 장점은, OS 에 직접적으로 설치하지 않아 종속성(프로그램 간 충돌)으로 인한 문제를 제거할 수 있고, 다른 사용자가 구성한 모범 케이스를 쉽게 구성할 수 있다는 장점이 있습니다.

또한, 기존에 1개 서버에 구성하는 경우, 서버에 있는 모든 기능들을 침투할 수 있지만, 컨테이너는 격리된 환경을 제공하여, 침투 하는 경우, 해당 컨테이너 내에서의 기능만 악용할 수 있는 장점등이 있습니다.

하지만, Windows 환경에서는 컨테이너가 최적화된 환경이 아니기에, 기존의 OS 커널을 원활하게 이용하지 못한다는 단점이 있었습니다. 이에 따라 장기적인 사용 입장에서는 Linux OS 를 가져가는 것이 맞지만, 최근 윈도우의 Hyper-v 그리고 Hyper-v 를 통한 WSL 의 발전으로 인해 속도 저하가 거의 없는 Linux 환경을 실행할 수 있게 되었습니다.

이에 따라, 직접적인 Ubuntu 가 아닌, WSL 을 선택하게 되었습니다. (또한, 가정집인 만큼 전력 사용량을 고려해야 하는데, 우분투의 경우 전력 효율화 면에서 윈도우 보다 최적화가 부족하다고 알려져 있습니다. )

[참고] WSL Ubuntu vs Native Ubuntu ( 직접 설치 ) 실제로, Native(직접 설치) 된 Linux와의 벤치마크 비교시, CPU만 놓고 보는 벤치마크에서는 직접 설치하는 방식과의 차이가 거의 없어질 만큼 발전되었습니다.

CPU Bechamrk

[출처] https://www.phoronix.com/review/windows11-wsl2-zen4/2

02. 시스템 구성 환경

01) OS 시스템 구성

WSL 구조

우리는 빨간 환경을 구성할 예정입니다.

  • 도커가 아닌 쿠버네티스를 사용하는 이유 단순히 워드프레스만 구성하려는 것이 아닌, 여러 컨테이너(모니터링, 여러 웹서버) 등을 운영하려고 하기에 컨테이너 오케스트레이션 도구인 쿠버네티스를 선택 하였습니다.
  • Micro k8s 를 선택한 이유 일반적으로 쿠버네티스 환경은 물리적으로 분리된 서버들 여러대로 구성을 해야 합니다. 이를 통해 HA 를 조금 더 안정적으로 운영할 수 있습니다. 하지만, 지금 환경은 전력을 최소화 하는 것이 목적인 만큼 실제 장비를 늘리지 않아야 하는 것과, WSL을 늘리지는 않고자 하였습니다. 그렇기에 1개 서버로 전체를 운영하는 환경이 필요했고, Microk8s , minikube, k3s를 비교한 결과 현재 / 향후의 추가할 기능들을 고려해 Microk8s 를 선택 하였습니다.
microk8s 구성

[출처] https://microk8s.io/compare

02) 웹서버 구성

Wordpress 구조

[CloudFlare]

DDOS 방지 및 WAF 구성을 위해 구성 하였습니다. 부가적으로 무료 CDN 을 통해 해외에서 접속시 속도 향상 적인 부분도 있으며, SSL 인증을 자체적으로 DNS 단에서 해주기 때문에, Cert-manager 등의 구성이 필요없다는 장점도 있습니다.

[Ingeress]


Ingress 를 설정하는 것은, 하나의 서버에서 여러개의 웹서버를 운영하는 경우 유용 합니다. a.com / b.com / c.com 을 운영하는 경우, 서버는 1대 임으로 향하는 IP 는 항상 x.x.x.x 인데, 이때 IP 가 동일하더라도 URL 별로 접근하는 서버를 분리할수 있습니다. 또한, 외부에 웹서버가 직접적으로 노출되지 않는 다는 장점도 있습니다.

03) 외에 쿠버네티스 관련 도구

  • 모니터링 도구
    • Prometheus Stack

03. WSL 환경 구성하기

01) 사전 설정

(01) 제어판 접근

[윈도우 + R] → control

(02) 프로그램 → widnows 기능 켜기/끄기

(03) Hyper-v , Linux용 Windows 하위 시스템 선택 → 확인

(04) 재부팅

02) WSL 설정

(01) WSL 설치

https://apps.microsoft.com/store/detail/windows-subsystem-for-linux/9P9TQF7MRM4R

[Option]

(02) Bridge 네트워크로 구성

기본적으로 WSL은 별도의 내부 네트워크 환경에서 구성 됩니다. 이 상태는 윈도우를 통해서만 접근이 가능한 상태라, 외부에 노출 시키기 위해서는 윈도우 방화벽에 별도로 세팅하고 포트 포워딩을 통한 작업이 필요하게 됩니다.

저는 이런 작업을 없애고자 처음부터 WSL이 공유기에 직접 붙을 수 있도록 구성 하고자 세팅 하였습니다.

[1] hyper-v 관리자 접근

[2] 가상 스위치 관리자 (작업탭 하단 3시 방향 )

[3] 새 가상 네트워크 스위치 → 가상 스위치 만들기

  • 유형은 외부로 설정해주세요.

[4] 가상 스위치 명을 지정한 후 / 네트워크 어댑터 카드를 선택 합니다.

  • 이름 : wsl_Public (원하는 이름)
  • 네트워크 카드 잘 선택 할것 (현재 네트워크에 속성을 보면 어떤 랜카드 인지 알수 있습니다. )

[5] .wslconfig 파일 만들기

생성 해야하는 폴더 : C:\Users\[사용자 이름]\.wslconfig

파일 내용

[WSL2]
kernelCommandLine=ipv6.disable=1
networkingMode = bridged
vmSwitch = wsl_Public

참고 사항

만약, 스위치 명이 wsl_Public 이 아닌 다른 이름으로 지정 하셨다면 내용을 바꿔 주세요. ipv6.disable=1 을 설정하는 이유는 외부에서 WSL 내부 리눅스나 대상들을 접근할 때 xxx.xxx.xxx.xxx(IPv4) 로 접근 하였으나, ipv6 주소로 변환하여 접근하려고 하는 부분이 있습니다.

만약, 여러분의 스위치나 랜카드가 IPv6 설정이 되어 있지 않은 경우 내부 네트워크가 정상적으로 동작하지 않기에 IPv6 로 통신하고자하는 경우가 아니라면 위의 방식을 설정하는 것이 좋습니다.

03) Ubuntu 설치

[Powershell]

wsl --install Ubuntu 

→ 이후 계정 비밀번호 설정 하면 완료 됩니다.

초기에 설치되면 바로 실행 되며, 추후에 우분투 접근시 아래 명령어로 접속해 주세요.

[참고로, wsl 을 쳤을때 접근 되는 대상은 Default 로 설정된 대상으로 여러 WSL 이 있는 경우 별도 설정이 필요합니다.]

wsl

04. Ubuntu 설정

01) 시스템 설정 변경

(01) 우분투 업데이트 서버 변경

WSL 의 기본 우분투 업데이트 서버는 미국 우분투 서버로 설정 됩니다. 최근 국내에서 접속하려고 하면 접속을 실패하는 경우도 많고 다운로드에 많은 시간이 소요됨으로, 국내 서버로 변경하는 것이 좋습니다.

sudo sed -i 's/archive.ubuntu/mirror.kakao/' /etc/apt/sources.list
sudo apt-get update

(02) SSH 설정 (필요한 경우)

sudo apt-get openssh-server

02) Micro K8s 설치 (쿠버네티스 설치)

[참고 사이트] https://microk8s.io/docs/getting-started

sudo snap install microk8s --classic

05. 쿠버네티스 기본 설정

01) Kubectl 변경 설치

기본적으로 microk8s 는 microk8s.kubectl 을 통해서 명령할 수 있도록 하지만, 실제 기능은 kubectl 보다 부족한 편입니다. 이에 따라, 실제 kubectl 에서는 동작하는 명령이 정상적으로 동작하지 않는 경우가 있습니다. 그럼으로, kubectl 을 직접 설치해서 사용하시는 것을 추천 드립니다.

(01) Kubectl 설치

리눅스에 kubectl 설치 및 설정

OS 마다 다르기에 위의 URL 을 참고해 주세요.

(02) config 파일 가져오기

kubectl 을 사용하려면 접근하려는 서버의 microk8s 접속 정보가 필요합니다. 이를 가져오기 위한 명령어를 작성해 봅시다.

microk8s config > ~/.kube/config

(03) 명령 테스트

kubectl get pod 

(04) 쿠버네티스 명령어 자동완성 설정 및 단축 하기

https://kubernetes.io/docs/reference/kubectl/cheatsheet/

02) Ingress 설치 및 테스트

(01) nginx-Ingress 설치

https://microk8s.io/docs/addon-ingress

microk8s enable ingress

(02) 기본 테스트

curl localhost

[결과]

kerneltrek@nuci7:~$ curl localhost
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

→ 만약 timeout 이나 반응이 없다면, WSL 설정 부분 중 ipv6 비활성화 (.wslconfig) 가 되지 않은 경우에 주로 발생합니다.

(03) 정상 동작 테스트

https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/

[Test 용 Web 배포 ]

kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0
kubectl expose deployment web --type=ClusterIP --port=8080

Type은 LB , ClusterIP , NodePort 상관 없습니다. 개인적으로 Ingress 를 사용하신다면 ClusterIP 로 URL 을 통한 접근 외에 제한하는 것이 좋을 수 있습니다.

[Test 용 Ingress 설정 ]

kubectl apply -f <https://k8s.io/examples/service/networking/example-ingress.yaml>
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    - host: hello-world.info
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 8080

→ host 정보가 URL 에 해당하는 부분 입니다. 추후 Ingress 설정시 여러분의 URL 로 변경해주세요.

[확인]

curl --resolve "hello-world.info:80:127.0.0.1" -i <http://hello-world.info>
HTTP/1.1 200 OK
Date: Wed, 26 Jul 2023 03:15:08 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 60
Connection: keep-alive

Hello, world!
Version: 1.0.0
Hostname: web-548f6458b5-9l9gk

[삭제]

kubectl delete deployment web
kubectl delete service web
k delete ingress example-ingress

06. WordPress 구성

01) Secret 설정 (비밀번호 / 계정 / DB 등)

(01) 설정 파일 생성

워드프레스에서 사용할 데이터 베이스의 대한 기본적인 정보를 설정 합니다.

  • [ ] 사이에 대상을 자신이 원하는 정보로 변경 합니다.
vim kustomization.yml
secretGenerator:
- name: mysql-password
  literals:
  - password=[mysql-password]
- name: mysql-user
  literals:
  - username=[mysql-user]
- name: mysql-user-password
  literals:
  - passworduser=[mysql-user-password]
- name: mysql-database
  literals:
  - database=[mysql-database]

(02) 반영

[명령어]

kubectl apply -k .

[결과]

$ kubectl apply -f ./                  

persistentvolume/mysql-pv created
persistentvolumeclaim/mysql-pv-claim created
persistentvolume/wordpress-pv created
persistentvolumeclaim/wordpress-pv-claim created

이 명령이 잘 먹히지 않는 다면 2가지를 확인하셔야 합니다.

  1. 파일명이 kustomization.yml 인지 ?
  2. microk8s.kubectl 을 사용하는지 ? ( microk8s.kubectl 은 이 명령이 kubectl 의 모든 명령을 수행하지는 못합니다. )

02) 데이터 베이스 / 워드 프레스 스토리지 생성

기본적으로 컨테이너로 구성한 서버는 컨테이너가 종료되면 가지고 있던 데이터 들도 다 사라지게 됩니다.

이를 방지하기 위해, 영구 적인 저장을 위한 설정을 합니다.

(01) DataBase 볼륨 생성

  • 기본 용량 : 50Gi [원하는 용량으로 변경하세요]
vim db-volume.yml
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
spec:
  storageClassName: db-storage
  capacity:
    storage: 80Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/var/lib/mysql"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: db-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 80Gi

(02) WordPress 생성

vim wp-volume.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: wordpress-pv
spec:
  storageClassName: wp-storage
  capacity: 
    storage: 50Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/var/www"
---    
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wordpress-pv-claim
spec:
  storageClassName: wp-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Gi

(03) 생성 및 확인

[생성 명령어]

kubectl apply -f db-volume.yml
kubectl apply -f wp-volume.yml

[확인 명령어]

kubectl get pv,pvc

[결과 확인] → Status 가 bound 되어야 함

jay@JayLees-MacBook-Pro  ~/Documents/wp-setting/003.volumes   main  k get pv,pvc        
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                               STORAGECLASS        REASON   AGE
persistentvolume/pvc-2e9bb24f-a2e3-42e9-96bd-8309cd9ceda6   20Gi       RWX            Delete           Bound    container-registry/registry-claim   microk8s-hostpath            161m
persistentvolume/mysql-pv                                   80Gi       RWO            Retain           Bound    default/mysql-pv-claim              db-storage                   36s
persistentvolume/wordpress-pv                               50Gi       RWO            Retain           Bound    default/wordpress-pv-claim          wp-storage                   36s

NAME                                       STATUS   VOLUME         CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/mysql-pv-claim       Bound    mysql-pv       80Gi       RWO            db-storage     36s
persistentvolumeclaim/wordpress-pv-claim   Bound    wordpress-pv   50Gi       RWO            wp-storage     36s

참고로, 다중 노드를 사용하는 경우 이 방식이 적합하지 않습니다.

[ 다중 노드를 사용하는 경우 SMB 를 통해서 파일을 저장할 서버에 공유 폴더를 생성 하고 해당 폴더를 지정하여 공유하는 것을 추천 드립니다. ]

03) mysql & 워드프레스 생성

(01) 시크릿 정보 확인하기

[명령어]

kubectl get secret
jay@JayLees-MacBook-Pro  ~/Documents/wp-setting/004.deployment   main  k get secrets                          
NAME                             TYPE     DATA   AGE
mysql-database-86m8k7bm58        Opaque   1      10m
mysql-password-f79kbg8g42        Opaque   1      10m
mysql-user-hhd5dbh6m7            Opaque   1      10m
mysql-user-password-97b57kg42k   Opaque   1      10m

우리는 DB 비밀번호나 여러 정보들을 이미 설정해 두었습니다. 해당 정보를 이용하기 위해, 시크릿의 대한 정보를 미리 확인 합니다.

(02) 데이터베이스 정보

아래 내용 중 name 부분은 본인의 secret 정보를 입력해 주세요

[참고 자료]

  - name: MYSQL_ROOT_PASSWORD
    valueFrom:
      secretKeyRef:
        name: **[본인의 시크릿 번호 입력]**
        key: password
vim mysql-deployment.yml
apiVersion: v1
kind: Service
metadata: 
  name: mysql-wp
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress
    tier: mysql
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-wp
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: mysql
    spec:
      containers:
      - image: mysql:latest
        name: mysql
        env:
        - name: MYSQL_DATABASE
          valueFrom:
            secretKeyRef:
              name: mysql-database-86m8k7bm58
              key: database
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-password-f79kbg8g42
              key: password
        - name: MYSQL_USER
          valueFrom:
            secretKeyRef:
              name: mysql-user-hhd5dbh6m7
              key: username
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-user-password-97b57kg42k
              key: passworduser
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim

(03) 워드프레스 디플로이먼트 정보

vim wordpress-deployment.yml
apiVersion: v1
kind: Service
metadata:
  name: wordpress
spec:
  ports:
    - port: 80
  selector:
    app: wordpress
    tier: web
  type: ClusterIP

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: web
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: web
    spec:
      containers:
      - image: wordpress
        name: wordpress
        env:
        - name: WORDPRESS_DB_HOST
          value: mysql-wp:3306
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-user-password-97b57kg42k
              key: passworduser
        - name: WORDPRESS_DB_USER
          valueFrom:
            secretKeyRef:
              name: mysql-user-hhd5dbh6m7
              key: username
        - name: WORDPRESS_DB_NAME
          valueFrom:
            secretKeyRef:
              name: mysql-database-86m8k7bm58
              key: database
        ports:
        - containerPort: 80
          name: wordpress
        volumeMounts:
        - name: persistent-storage
          mountPath: /var/www/html
      volumes:
      - name: persistent-storage
        persistentVolumeClaim:
          claimName: wordpress-pv-claim

(04) 배포 확인

kubectl apply -f mysql-deployment.yml
kubectl apply -f wordpress-deployment.yml

[확인]

kubectl get pod, svc
jay@JayLees-MacBook-Pro  ~/Documents/wp-setting/004.deployment   main  k get pod,svc         
NAME                             READY   STATUS    RESTARTS   AGE
pod/mysql-wp-7d5798b8c4-hnvp6    1/1     Running   0          9m43s
pod/wordpress-5bc7c448f6-4vrrf   1/1     Running   0          98s

NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/kubernetes   ClusterIP   10.152.183.1     <none>        443/TCP    3h9m
service/mysql-wp     ClusterIP   10.152.183.191   <none>        3306/TCP   18m
service/wordpress    ClusterIP   10.152.183.122   <none>        80/TCP     98s

→ 만약 Status가 Running이 아닌 경우 dsecribe 로 확인 해주세요.

[체크]

kubectl describe pod [아닌 대상]

(아래 Case 는 secret 정보가 다른 경우 발생하는 에러 입니다. secret name 을 변경 하면 정상화 됩니다. )

kubectl describe pod mysql-wp-7d5798b8c4-hnvp6
Events:
  Type     Reason     Age               From               Message
  ----     ------     ----              ----               -------
  Normal   Scheduled  60s               default-scheduler  Successfully assigned default/mysql-wp-9b7ddf788-jnqjw to nuci7
  Normal   Pulled     36s               kubelet            Successfully pulled image "mysql:latest" in 23.940980225s (23.940986625s including waiting)
  Normal   Pulled     33s               kubelet            Successfully pulled image "mysql:latest" in 1.658573801s (1.658583301s including waiting)
  Normal   Pulled     18s               kubelet            Successfully pulled image "mysql:latest" in 1.717353538s (1.717363638s including waiting)
  Normal   Pulling    4s (x4 over 60s)  kubelet            Pulling image "mysql:latest"
  Normal   Pulled     2s                kubelet            Successfully pulled image "mysql:latest" in 1.657823986s (1.657868686s including waiting)
  Warning  Failed     2s (x4 over 36s)  kubelet            Error: secret "mysql-password" not found

04) ingress 설정

이제 워드프레스에 접근할 수 있는 ingress 를 만들어 봅시다.

제 샘플중 아래 기록된 부분만 변경하면 됩니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wp-ingress
  annotations:
    nnginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: www.jayslog.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: wordpress
                port:
                  number: 80
kubectl get ingress
jay@JayLees-MacBook-Pro  ~/Documents/wp-setting/005.ingress   main  > kubectl get ingress         
NAME                CLASS    HOSTS             ADDRESS   PORTS   AGE
wordpress-ingress   public   www.jayslog.com             80      3s

[현재 보시는 사이트가 위의 방식으로 구성 하였습니다. ]

클라우드 플레어 설정과 다음 설정 사항은 다음 포스팅을 참고해 주세요.

117 thoughts on “[WSL & K8S] 워드프레스 블로그 구축 하기”

  1. hey there and thank you for your information – I’ve definitely picked
    up something new from right here. I did however expertise some technical issues using this
    website, as I experienced to reload the web site lots of times
    previous to I could get it to load properly. I had been wondering if your web host
    is OK? Not that I’m complaining, but sluggish loading instances
    times will sometimes affect your placement in google and could damage your high quality score if advertising
    and marketing with Adwords. Anyway I’m adding this RSS to my e-mail and can look
    out for a lot more of your respective interesting content.
    Make sure you update this again soon.. Escape rooms hub

    응답
  2. I seriously love your website.. Excellent colors & theme. Did you develop this website yourself? Please reply back as I’m looking to create my own personal website and would love to find out where you got this from or just what the theme is named. Appreciate it!

    응답
  3. May I simply say what a relief to uncover somebody that really knows what they are discussing on the internet. You definitely realize how to bring an issue to light and make it important. More people should look at this and understand this side of your story. I was surprised that you’re not more popular since you certainly have the gift.

    응답

Leave a Comment