재형이의 성장통 일지
  • Kubernetes The Hard Way
    2024년 01월 05일 23시 18분 37초에 업로드 된 글입니다.
    작성자: 재형이
    반응형
     

    GitHub - kelseyhightower/kubernetes-the-hard-way: Bootstrap Kubernetes the hard way on Google Cloud Platform. No scripts.

    Bootstrap Kubernetes the hard way on Google Cloud Platform. No scripts. - GitHub - kelseyhightower/kubernetes-the-hard-way: Bootstrap Kubernetes the hard way on Google Cloud Platform. No scripts.

    github.com

    • 실습을 진행하기에 앞서 필요한 컴퓨팅 리소스들은 AWS 클라우드를 이용하여 배포할 생각입니다.
    • 위에 실습에서는 gcloud를 사용하고 저희는 AWS를 사용하기 때문에 여기 링크를 참조해주세요. aws cli 를 사용한 실습입니다.
    • https://github.com/go4real/kubernetes-the-hard-way-aws
     

    GitHub - go4real/kubernetes-the-hard-way-aws: AWS version of Kelsey's kubernetes-the-hard-way

    AWS version of Kelsey's kubernetes-the-hard-way. Contribute to go4real/kubernetes-the-hard-way-aws development by creating an account on GitHub.

    github.com

    • 총 3개의 controlplane과 3개의 worker node를 배포할 것 입니다.
    • 배포 시에는 IaC를 사용할 것이며 Terraform을 사용하도록 하겠습니다.
    • 참고로 위 aws cli를 사용한 실습서를 참고하실 경우에는 VPC CIDR이랑 서브넷 CIDR, 그리고 controller.tf, worker.tf에 있는 ip들을 수정해주셔야 합니다.

    실습 구성도

    • 실습을 위해 모두 Public으로 구성...ㅎ

    파일 구조

     

    더보기
    ########### main.tf ###########
    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 5.31.0"
        }
      }
    }
    
    provider "aws" {
      region = "ap-northeast-2"
    }
    
    module "dev" {
      source = "../../"
    
      # project
      project_name = var.project_name
      environment = var.environment
    
      # VPC
      cidr_vpc = var.cidr_vpc
      cidr_public = var.cidr_public
    
      # EC2
      instance_type = var.instance_type
      key_name      = var.key_name
      volume_size   = var.volume_size
    }
    
    ########### variables.tf ###########
    # project
    variable "project_name" { default = "kubernetes-the-hard-way" } 
    variable "environment" { default = "kubernetes" }
    
    # VPC
    variable "cidr_vpc"        { default = "10.240.0.0/16"}
    variable "cidr_public"    { default = "10.240.0.0/24" }
    
    # EC2
    variable "key_name" { default = "linux-practice" }
    variable "instance_type" { default = "t3.micro" }
    variable "volume_size"   { default = 50 }
    
    ########### vpc.tf ###########
    # VPC
    resource "aws_vpc" "vpc" {
      cidr_block           = "${var.cidr_vpc}"
      enable_dns_support   = true
      enable_dns_hostnames = true
    
      tags = {
        Name = "${var.project_name}"
      }
    }
    
    # Subnet
    resource "aws_subnet" "public" {
      vpc_id                  = aws_vpc.vpc.id
      availability_zone       = "ap-northeast-2a"
      cidr_block              = "${var.cidr_public}"
      map_public_ip_on_launch = true
    
      tags = {
        Name = "${var.environment}"
      }
    }
    
    # Internet Gateway
    resource "aws_internet_gateway" "igw" {
      vpc_id = aws_vpc.vpc.id
    
      tags = {
        Name = "${var.environment}"
      }
    }
    
    # Security group
    resource "aws_default_security_group" "default" {
      vpc_id = aws_vpc.vpc.id
    
      tags = {
        Name = "${var.environment}"
      }
    }
    
    # Route table
    resource "aws_route_table" "public" {
      vpc_id = aws_vpc.vpc.id
    
      tags = {
        Name = "${var.environment}"
      }
    }
    
    resource "aws_route_table_association" "public" {
      subnet_id      = aws_subnet.public.id
      route_table_id = aws_route_table.public.id
    }
    
    resource "aws_route" "public" {
      route_table_id         = aws_route_table.public.id
      gateway_id             = aws_internet_gateway.igw.id
      destination_cidr_block = "0.0.0.0/0"
    }
    
    ########### controllers.tf ###########
    # Controller Instance
    resource "aws_instance" "controllers" {
      # count = length(data.aws_network_interfaces.controllers.ids)
        for_each = toset([
        "0",
        "1",
        "2"
      ])
    
      associate_public_ip_address = true
      vpc_security_group_ids = [aws_security_group.sg.id]
      subnet_id = aws_subnet.public.id
      source_dest_check = false
      ami = data.aws_ami.ubuntu.id
      key_name = "${var.key_name}"
      instance_type = "${var.instance_type}"
      private_ip = "10.240.0.1${each.key}"
      user_data = <<-EOF
                name=controller-${each.key}
                EOF
      ebs_block_device {
        volume_size = "${var.volume_size}"
        delete_on_termination = true
        device_name = "/dev/sda1"
        tags = {
          Name = "${var.environment}"
        }
      }
      tags = {
        Name = "controller-${each.key}"
        Type = "controller"
      }
    }
    
    ########### workers.tf ###########
    # Controller Instance
    resource "aws_instance" "workers" {
        for_each = toset([
        "0",
        "1",
        "2"
      ])
    
      associate_public_ip_address = true
      subnet_id = aws_subnet.public.id
      source_dest_check = false
      vpc_security_group_ids = [aws_security_group.sg.id]
      ami = data.aws_ami.ubuntu.id
      key_name = "${var.key_name}"
      instance_type = "${var.instance_type}"
      private_ip = "10.240.0.2${each.key}"
      user_data = <<-EOF
                name=worker-${each.key}|pod-cidr=10.200.${each.key}.0/24
                EOF
      ebs_block_device {
        volume_size = "${var.volume_size}"
        delete_on_termination = true
        device_name = "/dev/sda1"
        tags = {
          Name = "${var.environment}"
        }
      }
      tags = {
        Name = "worker-${each.key}"
      }
    }
    
    ########### nlb.tf ###########
    # Network Load Balancer
    resource "aws_lb" "nlb" {
      name               = "${var.environment}"
      internal           = false
      load_balancer_type = "network"
      subnets            = [aws_subnet.public.id]
    
      enable_deletion_protection = false
    
      tags = {
        Name = "${var.environment}"
      }
    }
    
    # Target Group
    resource "aws_lb_target_group" "tg" {
      name     = "${var.environment}"
      port     = 6443
      protocol = "TCP"
      vpc_id   = aws_vpc.vpc.id
      target_type = "ip"
    }
    
    resource "aws_lb_target_group_attachment" "tg" {
        for_each = toset([
        "0",
        "1",
        "2"
      ])
    
      target_group_arn = aws_lb_target_group.tg.arn
      target_id        = data.aws_instances.controllers.private_ips[each.key]
    
      depends_on  = [data.aws_instances.controllers]
    }
    
    output "controllers-private_ips" {
      value = data.aws_instances.controllers.private_ips
    }
    
    # Listener
    resource "aws_lb_listener" "listener" {
      load_balancer_arn = aws_lb.nlb.arn
      port              = "443"
      protocol          = "TCP"
    
      default_action {
        type             = "forward"
        target_group_arn = aws_lb_target_group.tg.arn
      }
    }
    
    ########### security_groups.tf ###########
    # Security Group
    resource "aws_security_group" "sg"{
        name        = "${var.environment}"
        description = "Kubernetes security group"
        vpc_id      = aws_vpc.vpc.id
    
        ingress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["10.240.0.0/16"]
      }
    
        ingress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["10.200.0.0/16"]
      }
    
        ingress {
        from_port   = 22
        to_port     = 22
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
      }
    
        ingress {
        from_port   = 6443
        to_port     = 6443
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
      }
    
        ingress {
        from_port   = 443
        to_port     = 443
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
      }
    
        ingress {
        from_port   = -1
        to_port     = -1
        protocol    = "icmp"
        cidr_blocks = ["0.0.0.0/0"]
      }
    
      egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
      }
    
      tags = {
        Name = "${var.environment}"
      }
    }
    
    ########### data.tf ###########
    # Data Source
    
    # Ubuntu Image ID
    data "aws_ami" "ubuntu" {
      most_recent      = true
    
      filter {
        name   = "root-device-type"
        values = ["ebs"]
      }
    
      filter {
        name   = "architecture"
        values = ["x86_64"]
      }
    
      filter {
        name   = "name"
        values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
      }
    
      owners           = ["099720109477"]
    }
    
    # Controller EC2s
    data "aws_instances" "controllers" {
      instance_tags = {
          Type = "controller"
      }
      depends_on  = [aws_instance.controllers]
    }

     

    • 테라폼 경험이 많이 없어서 깔끔하진 못 합니다 ㅜㅜ
      조언해주시면 감사하겠습니다!!!
    • Bastion Host 코드를 추가하시거나 따로 생성해서 이용해주시면 되겠습니다. (깜빡함ㅎ)

    tmux  기본 명령어

    • 기본 패키지 관리툴을 사용해서 설치해주면 된다.
    • 기본 명령어
    # 세션 리스트 확인 : tmux ls
    
    # 세션 생성 : tmux new -s example
    
    # 세션 접속 : tmux attach -t example
    
    # 창 분할하기 : ctrl+B 누르고 shift+"
    
    # 분할된 창 이동하기 : ctrl+B 누르고 화살표로 이동
    
    # 분할된 창 크기 조절 : ctrl+B 누른 상태에서 화살표로 조절
    
    # 세션 잠깐 나가기 : ctrl+B 누르고 D
    
    # 분할된 창들 싱크 맞추기 (키보드 입력이 동시에 됨) : 
    ### ctrl+B 누르고 shift+: 그러면 입력창이 나옴
    ### 거기에 set synchronize-pane on 입력
    ### 싱크 풀려면 set synchronize-pane off 입력
    ### 입력창에서 화살표 위쪽을 누르면 이전 명령어 볼 수 있다

    학습 목표

    • 사실 실습은 위에 깃주소에서 그대로 따라하면 되기 때문에 어렵진 않지만, 왜 이런 과정들을 거쳐야 하는지에 대해서 정리를 해볼 것이다.
    • 실습 위주가 아닌 이론 위주로!!!
    • 실습은 위에 테라폼 코드로 aws에 배포하고 한번 따라해보시길 바랍니다.

    주의사항

    https://jink1982.tistory.com/123

     

    [Unix/Linux] vi, vim ^M 제거

    vi, vim ^M 제거윈도우에서 작성한 파일을 Unix/Linux상에 올리면 개행 문자가 깨져서 ^M가 보이는 경우를 봤을 것이다. 이것은 윈도우에서는 CRLF 가 개행인데 Linux/Unix에서는 LF가 개행이라 나타나는

    jink1982.tistory.com

    • 윈도우에서 실습을 진행할 경우 vim 코드 작성할 때 ^M 때문에 오류가 발생하는 경우가 있더라구요. 혹시 모르니 한번 확인해보세요!!!

    04-certificate-authority

    https://github.com/go4real/kubernetes-the-hard-way-aws/blob/master/docs/04-certificate-authority.md

    쿠버네티스의 기본적인 아키텍쳐

    • 쿠버네티스는 기본적으로 PKI (Public Key Infrastructure)를 통해 API Server와 통신을 하게 된다.
    • cfssl 툴을 통해 ca 인증서를 만들어서 etcd, kube-apiserver, kube-controller-manager, kube-scheduler, kubelet 및 kube-proxy와 같은 구성 요소들이 서로 tls 인증을 할 수 있게 구성해야 한다.
    • CA : 믿을 수 있는 인증 기관
      Admin : 쿠버네티스에 접속할 때 사용되는 어드민 계정
      Kubelet : 워커노드에서 실제로 컨테이너들을 배치하고 관리하는 역할을 함
      Controller Manager : 다양한 컨트롤러들을 관리함, 주요 컨트롤러 : ReplicaSet, Deployment, StatefulSet
      Kube Proxy : 새로운 서비스들이 생성되거나 Pod가 추가될 때, 동작하고 있는 워커노드의 iptables에 룰을 추가함
      Scheduler : 클러스터 내에서 애플리케이션 파드를 적절한 노드에 스케줄링하는 역할을 함
      API Server : 각 컴포넌트 클라이언트들이 서로 통신할 때 중간에서 인증 절차를 확인하고 이어주는 역할을 함
      Service Account for token : 서비스 어카운트 토큰을 생성할 때 사용한다고 함
      etcd : 키•밸류 형태의 데이터를 저장하는 스토리지, 어떤 Pod가 어느 Node에 저장되어 있는지 각 Pod들 구성 등 저장
      📢 etcd는 가용성을 위해 다중 클러스터 구성 시 홀수개로 구성해야 함, 왜냐하면 데이터의 일관성을 위해 RAFT 합의 알고리즘을 사용하기 때문 (과반수로 primary 클러스터를 정하는데 짝수이면 과반수가 아닐 경우가 생김)

    05-kubernetes-configuration-files

    • Kubernetes 클라이언트가 Kubernetes API 서버를 찾고 인증할 수 있도록 하는 kubeconfig 구성 파일을 생성한다.
    • kubeconfig 파일 : kubectl 명령어가 api-server에 접근할 때 사용할 인증 정보를 가지고 있다.
    • 기본적으로 kubeconfig 파일은 $HOME/.kube/config에 저장이 됩니다.
    • kubeconfig 구조
    apiVersion: v1
    kind: Config
    
    preferences: {}
    
    clusters:
    - name: cluster.local
      cluster:
        certificate-authority-data: LS0tLS1...
        server: https://127.0.0.1:6443
    - name: mycluster
      cluster:
        server: https://1.2.3.4:6443
    
    users:
    - name: myadmin
    - name: kubernetes-admin
      user:
        client-certificate-data: LS0tLS1...
        client-key-data: LS0tLS1...
    
    contexts:
    - context:
        cluster: mycluster
        user: myadmin
      name: myadmin@mycluster
    - context:
        cluster: cluster.local
        user: kubernetes-admin
      name: kubernetes-admin@cluster.local
    
    current-context: kubernetes-admin@cluster.local
    • clusters : 접속할 서버 정보와 관련된 것이며 서버는 여러개 있을 수 있다. 원한다면 쿠버네티스 클러스터를 여러개 설정할 수 있다.
      cluster.local : 설정하지 않아도 기본적으로 구성되는 클러스터 이름
      client-authority-data : kubectl이 어떤 서버에 요청할 것인지에 대한 정보와 CA 인증서를 base64로 인코딩한 것
      server : 실제 api-server의 주소
    • users : 인증받을 사용자
      name : 사용자의 계정명
      client-certificate-data, client-key-data : 사용자가 사용할 클라이언트의 인증서(공개키)와 개인키
    • context : users와 clusters의 조합. 어떤 컨텍스트를 선택하느냐에 따라 클러스터와 사용자가 결정된다.
    • current-context : 현재 인증에 사용하기 위한 현재 컨텍스트를 의미한다. 

    06-data-encryption-keys

    • Secrets는 다른 쿠버네티스 리소스와 마찬가지로, 평소엔 etcd에 저장되어 있다가 API 서버와 kubelet을 거쳐 컨테이너로 전달된다.
    • 보통은 권한을 가진 Client 또는 Component가 API 서버에 요청하여 Secret을 가져오지만 etcd에 직접 접근해서 Secret 값을 조회할 수 있다.
    ETCDCTL_API=3 etcdctl --cert /etc/kubernetes/pki/apiserver-etcd-client.crt --key /etc/kubernetes/pki/apiserver-etcd-client.key --cacert /etc/kubernetes/pki/etcd/ca.crt get /registry/secrets/default/secret1 | hexdump -C
    
    00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
    00000010  73 2f 64 65 66 61 75 6c  74 2f 73 65 63 72 65 74  |s/default/secret|
    00000020  31 0a 6b 38 73 00 0a 0c  0a 02 76 31 12 06 53 65  |1.k8s.....v1..Se|
    00000030  63 72 65 74 12 cd 01 0a  b1 01 0a 07 73 65 63 72  |cret........secr|
    00000040  65 74 31 12 00 1a 07 64  65 66 61 75 6c 74 22 00  |et1....default".|
    00000050  2a 24 39 38 30 30 34 65  38 61 2d 62 64 31 37 2d  |*$98004e8a-bd17-|
    00000060  34 62 62 65 2d 39 32 37  35 2d 39 62 32 63 66 32  |4bbe-9275-9b2cf2|
    00000070  33 62 35 66 32 62 32 00  38 00 42 08 08 fd a1 9a  |3b5f2b2.8.B.....|
    00000080  8f 06 10 00 7a 00 8a 01  62 0a 0e 6b 75 62 65 63  |....z...b..kubec|
    00000090  74 6c 2d 63 72 65 61 74  65 12 06 55 70 64 61 74  |tl-create..Updat|
    000000a0  65 1a 02 76 31 22 08 08  fd a1 9a 8f 06 10 00 32  |e..v1".........2|
    000000b0  08 46 69 65 6c 64 73 56  31 3a 2e 0a 2c 7b 22 66  |.FieldsV1:..,{"f|
    000000c0  3a 64 61 74 61 22 3a 7b  22 2e 22 3a 7b 7d 2c 22  |:data":{".":{},"|
    000000d0  66 3a 6d 79 6b 65 79 22  3a 7b 7d 7d 2c 22 66 3a  |f:mykey":{}},"f:|
    000000e0  74 79 70 65 22 3a 7b 7d  7d 42 00 12 0f 0a 05 6d  |type":{}}B.....m|
    000000f0  79 6b 65 79 12 06 6d 79  64 61 74 61 1a 06 4f 70  |ykey..mydata..Op|
    00000100  61 71 75 65 1a 00 22 00  0a                       |aque.."..|
    00000109
    • 'mykey'의 값이 'mydata' 인 것을 확인할 수 있습니다.
    • 이처럼 보안상의 이유로 etcd는 암호화하는 것이 좋습니다.
    ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
    
    cat > encryption-config.yaml <<EOF
    kind: EncryptionConfig
    apiVersion: v1
    resources:
      - resources:
          - secrets
        providers:
          - aescbc:
              keys:
                - name: key1
                  secret: ${ENCRYPTION_KEY}
          - identity: {}
    EOF
    • 해당 파일 경로를 kube-apiserver 옵션에 추가하면 됩니다.
      kube-apiserver --encryption-provider-config=/파일경로/encryption-config.yaml
    00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
    00000010  73 2f 64 65 66 61 75 6c  74 2f 73 65 63 72 65 74  |s/default/secret|
    00000020  31 0a 6b 38 73 3a 65 6e  63 3a 73 65 63 72 65 74  |1.k8s:enc:secret|
    00000030  62 6f 78 3a 76 31 3a 6b  65 79 31 3a 99 7b 00 83  |box:v1:key1:.{..|
    00000040  6a 16 af 2c 04 20 1f d9  db 72 57 f9 be 21 d1 49  |j..,. ...rW..!.I|
    00000050  3a e7 77 f7 8f af b5 c2  ed ad f2 16 fc 47 3e 7b  |:.w..........G>{|
    00000060  e0 84 3d 36 2b be 65 69  8e dd 0b 9d 96 a7 85 20  |..=6+.ei....... |
    00000070  b2 cb 6a a5 ee 19 7d 4e  bc 34 df 91 7f c6 39 32  |..j...}N.4....92|
    00000080  33 9c 17 06 dd 37 ed d0  02 7b 52 aa 50 60 c4 1f  |3....7...{R.P`..|
    00000090  9f a0 4f a7 c8 cd f7 38  a2 f3 57 32 14 29 8e 25  |..O....8..W2.).%|
    000000a0  c9 7f e9 83 57 26 fa 30  b9 57 fa f0 13 a2 c3 15  |....W&.0.W......|
    000000b0  a5 66 98 a4 53 b3 16 de  b0 85 bb ad 92 50 40 50  |.f..S........P@P|
    000000c0  6f a9 7d 8a c9 4a 9b 0e  c1 f7 47 5d 65 a7 03 09  |o.}..J....G]e...|
    000000d0  81 c2 63 4d ad 95 62 51  b0 62 8b 45 c4 c7 5a 7c  |..cM..bQ.b.E..Z||
    000000e0  45 bb 68 6d e8 3d 5b 59  28 f8 2d 37 e2 bd cb da  |E.hm.=[Y(.-7....|
    000000f0  b9 32 f4 37 35 3e 68 86  ef 33 d6 17 3a 4c af f8  |.2.75>h..3..:L..|
    00000100  de 6d 61 2e 67 7e 43 33  4d 15 ab a1 05 4d c1 af  |.ma.g~C3M....M..|
    00000110  76 00 78 20 1a f7 fb ea  23 70 df be 89 14 75 d9  |v.x ....#p....u.|
    00000120  76 88 03 60 38 16 3e 75  fe 6b b9 0f 75 d0 36 f1  |v..`8.>u.k..u.6.|
    00000130  57 e2 52 be a3 80 be 0e  5b 64 57 dc 78 8d ac 6f  |W.R.....[dW.x..o|
    00000140  74 e2 fd 6c 33 9a e6 30  34 47 0a                 |t..l3..04G.|
    0000014b
    • 암호화가 잘된 것을 확인할 수 있습니다.

    07-bootstrapping-etcd

    [Unit]
    Description=etcd
    Documentation=https://github.com/coreos
    
    [Service]
    Type=notify
    ExecStart=/usr/local/bin/etcd \\
      --name ${ETCD_NAME} \\
      --cert-file=/etc/etcd/kubernetes.pem \\
      --key-file=/etc/etcd/kubernetes-key.pem \\
      --peer-cert-file=/etc/etcd/kubernetes.pem \\
      --peer-key-file=/etc/etcd/kubernetes-key.pem \\
      --trusted-ca-file=/etc/etcd/ca.pem \\
      --peer-trusted-ca-file=/etc/etcd/ca.pem \\
      --peer-client-cert-auth \\
      --client-cert-auth \\
      --initial-advertise-peer-urls https://${INTERNAL_IP}:2380 \\
      --listen-peer-urls https://${INTERNAL_IP}:2380 \\
      --listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \\
      --advertise-client-urls https://${INTERNAL_IP}:2379 \\
      --initial-cluster-token etcd-cluster-0 \\
      --initial-cluster controller-0=https://10.0.1.10:2380,controller-1=https://10.0.1.11:2380,controller-2=https://10.0.1.12:2380 \\
      --initial-cluster-state new \\
      --data-dir=/var/lib/etcd
    Restart=on-failure
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target
    • 다른 옵션들은 서버 url과 인증서 파일, 키 파일 위치들을 넣어주는 옵션이고
      --data-dir은 데이터가 저장되는 파일 경로이다.
    • 그래서 etcd를 백업하고 restore할 때 이 부분을 새로 restore할 데이터 파일의 경로로 수정해주어야 한다.
    # 현재 상태를 백업 후 <backup-file-location> 경로에 데이터 저장
    ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
      --cacert=<trusted-ca-file> --cert=<cert-file> --key=<key-file> \
      snapshot save <backup-file-location>
      
    # 백업된 데이터 <backup-file-location>를 <data-dir-location>에 복구
    ETCDCTL_API=3 etcdctl --data-dir <data-dir-location> snapshot restore <backup-file-location>
    • 이렇게 복구한 후에 etcd 옵션에서 data-dir 부분을
      --data-dir=<data-dir-location> 으로 수정해주어야 함

    08-bootstrapping-kubernetes-controllers

    •  각 컨트롤플레인 노드에는 Kubernetes API 서버, 스케줄러, 컨트롤러 매니저와 같은 구성 요소가 설치됩니다.
    • 이 실습에서는 가용성을 위해 다중 클러스터 환경으로 구성을 하였고 로드 밸런싱을 위해 단일 인터페이스로 NLB를 생성해서 사용하고 있습니다. ALB가 아니라 NLB를 사용하는 이유는 header check를 할 필요가 없기 때문에 NLB를 사용하여 더 빠른 속도에 중점을 두었습니다.
    • 여기서는 binary 파일로 각 컴포넌트들을 서비스로 올려서 사용하고 있지만 static-pod를 이용하는 방법도 있습니다.
      기본 static-pod 경로 : /etc/kubernetes/manifests/
    • RBAC을 설정하는 부분은 지금까지 인증 절차를 거쳤으니 인가 절차를 통해 어떤 리소스에 어떤 권한을 가지고 접근할 수 있는지 부여하는 부분이다.

    09-bootstrapping-kubernetes-workers

    • 각 워커노드에는 runc, container networking plugins (CNI, Weave, Flannel, Calico 등), containerd, kubelet 및 kube-proxy와 같은 구성 요소가 설치됩니다.
    • kubernetes v1.18 이상은 socat과 conntrack 패키지가 필수이고 ipset 패키지는 설치를 권장하며, v1.18 미만은 모두 설치를 권장한다고 합니다.
      socat : socat은 의존성이 없는 두 채널 사이에서 양방향으로 데이터 전송을 중계
      conntrack : netfilter (리눅스 커널 기능 중 하나)가 네트워크에서 발생하는 커넥션에 대해 해당 내용들을 기록하고 추적하기 위한 모듈이다. 즉, iptables가 netfilter를 사용하기 때문에 iptables의 상태추적 모듈이라고 볼 수 있다. iptables가 수정하는 NAT 테이블과 같은 상태들을 추적함.
      ipset : 특정 ip를 차단하는데 사용하는 유틸리티인 것 같음...ㅎ (검색해도 사용법만 나오고 개념은 잘 안나오네요)
    sudo apt-get update
    sudo apt-get -y install socat conntrack ipset
    • swap : 메모리가 부족할 시 하드 디스크의 일부 공간을 활용하여 계속 작업을 할 수 있게 해줍니다. 당연히 RAM 보다 속도가 느림.
      기본적으로 swap이 활성화되면 kubelet이 시작되지 않는다. 쿠버네티스가 적절한 리소스 할당과 서비스 품질을 제공할 수 있도록 스왑을 비활성화하는 것이 좋다.
    # swap 상태 확인
    sudo swapon --show
    # swap 기능 off
    sudo swapoff -a

    10-configuring-kubectl

    • 관리자 사용자 자격 증명을 기반으로 kubectl 명령줄 유틸리티를 위한 kubeconfig 파일을 생성한다.
    # kubectl config 명령어로 kubeconfig 파일 생성
    kubectl config set-cluster kubernetes-the-hard-way \
      --certificate-authority=ca.pem \
      --embed-certs=true \
      --server=https://${KUBERNETES_PUBLIC_ADDRESS}:443
    
    kubectl config set-credentials admin \
      --client-certificate=admin.pem \
      --client-key=admin-key.pem
    
    kubectl config set-context kubernetes-the-hard-way \
      --cluster=kubernetes-the-hard-way \
      --user=admin
    
    kubectl config use-context kubernetes-the-hard-way
    
    # 확인
    kubectl get nodes
    
    NAME           STATUS   ROLES    AGE     VERSION
    ip-10-0-1-20   Ready    <none>   3m35s   v1.21.0
    ip-10-0-1-21   Ready    <none>   3m35s   v1.21.0
    ip-10-0-1-22   Ready    <none>   3m35s   v1.21.0

    11-pod-network-routes

     

    [번역] 쿠버네티스 네트워킹 이해하기#1: Pods

    해당 포스트는 제가 CKA 자격증 취득을 위해 쿠버네티스 network에 대해 공부할 때 굉장히 도움이 되었던 글로써, 많은 분들이 쿠버네티스를 공부하는데에 도움이 되길 바라는 마음에서 번역해 보

    coffeewhale.com

    • 아주 잘 정리가 되어 있습니다. 좋은 글 감사합니다 커피 고래님

    12-dns-addon

    • CoreDNS를 deployment로 배포
    kubectl config use-context kubernetes-the-hard-way
    kubectl apply -f https://storage.googleapis.com/kubernetes-the-hard-way/coredns-1.8.yaml
    
    serviceaccount/coredns created
    clusterrole.rbac.authorization.k8s.io/system:coredns created
    clusterrolebinding.rbac.authorization.k8s.io/system:coredns created
    configmap/coredns created
    deployment.apps/coredns created
    service/kube-dns created
    • 버네티스 클러스터 내 Pod에서 어떤 도메인을 찾고자 할 때 CoreDNS를 통해 질의를 하게 된다. 쿠버네티스 v1.12 부터 표준으로 채택되었다.
    • CoreDNS의 여러가지 기능은 Corefile설정 파일에 원하는 것들만 플러그인처럼 추가할 수 있다. 이 Corefile 파일은 ConfigMap 오브젝트에 저장되어 있다.
    kubectl -n kube-system describe configmap coredns
    
    Name:         coredns
    Namespace:    kube-system
    Labels:       <none>
    Annotations:  <none>
    
    Data
    ====
    Corefile:
    ----
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }
    • Pod를 하나 만들어서 /etc/resolv.conf 파일을 확인해보면
    nameserver x.x.x.x
    search default.svc.cluster.local svc.cluster.local cluster.local
    options ndots:5
      • 여기서 options ndots:5는 도메인에 포함된 점(.)의 개수가 5개부터 FQDN으로 취급을 의미한다.
      • 만약 어떤 Pod에서 k8s.example.coredns.ndot.options.test 같은 도메인을 찾으려고 한다면, 이 도메인은 FQDN이기 때문에 클라이언트는 k8s.example.coredns.ndot.options.test.형태로 DNS 서버에 질의를 하게 된다.
      • 그리고 search 옵션은 클라이언트가 DNS 서버로부터 결과가 없다는 응답을 받으면, search 도메인 목록을 차례대로 붙여가며 추가로 DNS 서버를 호출한다.
      • 위 상황에서는 최대 4번까지 서버를 호출할 수 있다.
      • 이 원리를 이용해서 정확하게 FQDN을 조회할 수 있다면 DNS 조회 횟수를 줄일 수 있다.
        예를 들어 coredns.test.example 같은 도메인을 찾는다고 한다면, 도메인 끝에 점(.)을 붙이지 않았더라도 요청하는 클라이언트가 자동으로 점(.)을 붙인 뒤 coredns.test.example.형태로 요청을 할 것 이다.
        하지만 실제로 저 도메인이 존재하지 않기 때문에 search 목록에 해당하는 default.svc.cluster.local, svc.cluster.local, cluster.local을 모두 붙여가며 추가로 DNS를 질의하게 된다.
        따라서 만약 조금이라도 CoreDNS 서버에 부하를 줄이고 싶다면, 외부 도메인을 호출할 때 마지막 점(.)을 추가하면 딱 1번만 DNS 서버에 질의할 수 있게 된다.

    마무리

    • 이전에 정보처리기사를 공부하면서 느꼈던 건데 공부를 할 때, 처음부터 완벽하게 하려고 하지 말고 일단 익숙해지는 것이 중요한 것 같다. 처음에는 용어들도 머릿속에 안들어오고 혼란스럽지만 꾸준히 반복해서 보다보면 점점 익숙해지고 보이지 않았던 것들이 보이게 되는 것 같다. 물론 어떻게 보면 일차원적이고 무식한 방법이라 시간이 오래 걸릴 수는 있겠지만 시험만을 위한 단기 기억 방식의 공부보다는 머릿속에 더 오래 남지 않을까...?
    • 공부할 건 많고 해보고 싶은 것들도 늘어나고 있지만 조금씩이라도 꾸준하게!!! 해야겠다.
    반응형
    댓글