/

Kubernetes 入门实战 Part1

01 初识 Docker

apt install -y docker.io
service docker start
usermod -aG docker ${USER}
docker version
docker info
docker ps
docker pull busybox
# show all images
docker images

https://docs.docker.com/get-started/overview/

02 被隔离的进程

docker pull alpine
docker run -it alpine sh
cat /etc/os-release

隔离资源,保证系统安全,提高资源的利用率。

资源隔离提供了三种技术:namespace、 cgroup、chroot(pivot_rott)。

03 容器化的应用

Build once, Run anywhere.

应用程序不再直接和操作系统打交道,而是封装成镜像,再交给容器环境去运行。

# remove image
docker rmi busybox
# 开启一个交互式操作的 Shell
docker run -it busybox
# 在后台运行
docker run -d busybox
# 为容器起一个名字
docker run -d --name xxx busybox
# 不保存容器,只要运行完毕就自动清除
docker run --rm busybox echo "hello docker"

docker stop xxx
# remove container
docker rm xxx

# show all container
docker ps -a
docker exec xxx echo "hello docker"

05 镜像仓库

用户名/应用名:标签 官方镜像用户名是 library

  • slim 经过精简的
  • fat 包含了较多的辅助工具
  • rc 候选版本,release candidate
docker build -t
docker tag ngx-app chronolaw/ngx-app:1.0
docker push chronolaw/ngx-app:1.0

save 和 load 这两个镜像归档命令:

docker save ngx-app:latest -o ngx.tar
docker load -i ngx.tar

06 打破次元壁

docker run -d --rm --name ubu phusion/baseimage:jammy-1.0.1
echo "hello" > a.txt
# a.txt 拷贝到容器 tmp
docker cp a.txt ubu:/tmp
docker exec -it ubu bash
# 考出容器
docker cp ubu:/tmp/a.txt ./b.txt

容器和主机共享本地目录:

# --mount
# -v /tmp:/tmp:ro 只读
docker run -d --rm -v /tmp:/tmp --name ubu phusion/baseimage:jammy-1.0.1
docker exec -it ubu bash
docker pull python:alpine
docker run -it --rm -v `pwd`:/tmp python:alpine sh

网络模式:null host bridge

# host
docker run -d --rm --net=host --name=ng nginx:alpine
docker exec ng ip addr
docker inspect ng | grep IPAddress
docker stop ng
# bridge 默认模式
docker run -d --rm --name=ng nginx:alpine
docker inspect ng | grep IPAddress
# "IPAddress": "172.17.0.3"
docker run -d --rm --name=rd redis
docker inspect rd | grep IPAddress
# "IPAddress": "172.17.0.4"

分配服务端口号

docker run -d -p 80:80 --rm nginx:alpine
docker run -d -p 8080:80 --rm nginx:alpine
# 分别“映射”到了两个容器里的 80 端口

07 玩转 Docker

Container Image Registry

https://registry.hub.docker.com/_/registry/

docker run -d -p 5000:5000 registry
# 使用 docker tag 命令给镜像打标签再上传
docker tag nginx:alpine 127.0.0.1:5000/nginx:alpine
docker push 127.0.0.1:5000/nginx:alpine
# 本次重新拉取测试
docker rmi 127.0.0.1:5000/nginx:alpine
docker pull 127.0.0.1:5000/nginx:alpine

https://docs.docker.com/registry/spec/api/

# Listing Repositories
curl 127.1:5000/v2/_catalog
# {"repositories":["nginx"]}
curl 127.1:5000/v2/nginx/tags/list
# {"name":"nginx","tags":["alpine"]}

registry 默认会把镜像存储在 Docker 内部目录 /var/lib/registry

搭建 WordPress:

docker run -d --rm \
--env MARIADB_DATABASE=db \
--env MARIADB_USER=wp \
--env MARIADB_PASSWORD=123 \
--env MARIADB_ROOT_PASSWORD=123 \
--name mariadb \
mariadb:10
# 进入 mariadb
docker exec -it mariadb mysql -uwp -p123
# show mariadb ip
docker inspect mariadb | grep IPAddress
# "IPAddress": "172.17.0.2"

docker run -d --rm \
--env WORDPRESS_DB_HOST=172.17.0.2 \
--env WORDPRESS_DB_USER=wp \
--env WORDPRESS_DB_PASSWORD=123 \
--env WORDPRESS_DB_NAME=db \
--name wp \
wordpress:5
docker inspect wp | grep IPAddress
# "IPAddress": "172.17.0.4"
vim wp.conf

server {
listen 80;
default_type text/html;
location / {
proxy_http_version 1.1;
proxy_set_header Host $host;
# wordpress server
proxy_pass http://172.17.0.4;
}
}
# 感觉可以直接用 wordpress 的 80,无需再代理一次
docker run -d --rm \
-p 80:80 \
-v `pwd`/wp.conf:/etc/nginx/conf.d/default.conf \
--name ng \
nginx:alpine
# show logs
docker logs mariadb
docker logs ng
docker logs wp

08 入门篇总结

docker pull alpine
docker run -it alpine sh
uname -a
# Linux de3852b4ec42 5.15.49-linuxkit #1 SMP Tue Sep 13 07:51:46 UTC 2022 x86_64 Linux

# Darwin V_YFANZHAO-MB1 19.6.0 Darwin Kernel Version 19.6.0: Tue Jun 21 21:18:39 PDT 2022; root:xnu-6153.141.66~1/RELEASE_X86_64 x86_64

构建自己的镜像:

https://github.com/chronolaw/k8s_study/blob/master/ch1/Dockerfile

ARG IMAGE_BASE="nginx"
ARG IMAGE_TAG="1.21-alpine"

FROM ${IMAGE_BASE}:${IMAGE_TAG}

ENV PATH=$PATH:/tmp
ENV DEBUG=OFF

COPY ./default.conf /etc/nginx/conf.d/

RUN cd /usr/share/nginx/html \
&& echo "hello nginx" > a.txt

EXPOSE 8081 8082 8083

WORKDIR /etc/nginx
docker build -t ngx-app:1.0 .
docker run -it --rm ngx-app:1.0 sh
docker save ngx-app:1.0 -o ngx.tar
docker load -i ngx.tar

09 Kubernetes 环境

容器编排 Container Orchestration

Kubernetes 就是一个生产级别的容器编排平台和集群管理系统。

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

minikube version
# minikube version: v1.29.0
# commit: ddac20b4b34a9c8c857fc602203b6ba2679794d3

# 统一实验环境
minikube start --kubernetes-version=v1.23.3
# 查看状态
minikube status
minikube node list
# minikube 192.168.49.2

# 登录到这个节点上
minikube ssh
uname -a
# Linux minikube 5.15.49-linuxkit #1 SMP Tue Sep 13 07:51:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
ip add
# minikube 管理 Kubernetes 集群环境
# kubectl 操作实际的 Kubernetes 功能
# install kubectl
minikube kubectl

minikube kubectl -- version
alias kubectl="minikube kubectl --"
kubectl version

# 启动一个镜像
kubectl run ngx --image=nginx:alpine

# like docker ps
kubectl get pod

https://kubernetes.io/zh/

10 Kubernetes 工作机制

Kubernetes 采用了现今流行的“控制面 / 数据面”(Control Plane / Data Plane)架构,集群里的计算机被称为“节点”(Node),可以是实机也可以是虚机,少量的节点用作控制面来执行集群的管理维护工作,其他的大部分节点都被划归数据面,用来跑业务应用。Master 节点实现管理控制功能,Worker 节点运行具体业务。

image
kubectl get node
# NAME STATUS ROLES AGE VERSION
# minikube Ready control-plane,master 38m v1.23.3

# 查看 master Component pod
kubectl get pod -n kube-system
# NAME READY STATUS RESTARTS AGE
# coredns-65c54cc984-4pckd 1/1 Running 0 40m
# etcd-minikube 1/1 Running 0 40m
# kube-apiserver-minikube 1/1 Running 0 40m
# kube-controller-manager-minikube 1/1 Running 0 40m
# kube-proxy-88wt2 1/1 Running 0 40m
# kube-scheduler-minikube 1/1 Running 0 40m
# storage-provisioner 1/1 Running 3 (6m39s ago) 40m
# worker node
minikube ssh
# show kube-proxy
docker ps |grep kube-proxy
# show kubelet, not exist docker
ps -ef|grep kubelet
# show addons list
minikube addons list

minikube dashboard
image

11 YAML

YAML 是 JSON 的超集。

Shell 脚本和 Dockerfile 可以很好地描述“命令式”(Imperative)。“声明式”(Declarative)注重结果。

apiserver 采用了 HTTP 协议的 URL 资源理念,API 风格也用 RESTful,被称为是“API 对象”了。

# 查看 kubectl api-service 支持的所有对象
kubectl api-resources

# 显示出详细的命令执行过程
kubectl get pod --v=9
# 自带的 API 文档 https://kubernetes.io/docs/reference/kubernetes-api/
kubectl explain pod
kubectl explain pod.metadata

# 命令式
kubectl run ngx --image=nginx:alpine
# 转换为 YAML 声名式
kubectl run ngx --image=nginx:alpine --dry-run=client -o yaml > ngx-pod.yml
kubectl apply -f ngx-pod.yml
kubectl delete -f ngx-pod.yml

12 Pod

为了解决这样多应用联合运行的问题,同时还要不破坏容器的隔离,就需要在容器外面再建立一个“收纳舱”。

apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: ngx
name: ngx
spec:
containers:
- image: nginx:alpine
name: ngx
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
image
vim ngx-pod.yml
kubectl apply -f ngx-pod.yml
kubectl logs ngx
kubectl get pod
kubectl describe pod ngx

# cp file to pod
echo 'aaa' > a.txt
kubectl cp a.txt ngx:/tmp

# exec need --
kubectl exec -it ngx -- sh

13 Job CronJob 离线业务

“单一职责”的意思是对象应该只专注于做好一件事情,不要贪大求全,保持足够小的粒度才更方便复用和管理。

“组合优于继承”的意思是应该尽量让对象在运行时产生联系,保持松耦合,而不要用硬编码的方式固定对象的关系。

kubectl create job echo-job --image=busybox --dry-run=client -o yaml > job.yml
apiVersion: batch/v1
kind: Job
metadata:
creationTimestamp: null
name: echo-job
spec:
template:
metadata:
creationTimestamp: null
spec:
containers:
- image: busybox
name: echo-job
resources: {}
# 补充输出
command: ["/bin/echo"]
args: ["hello", "world"]
restartPolicy: Never
status: {}
kubectl apply -f job.yml
kubectl get job
# NAME COMPLETIONS DURATION AGE
# echo-job 1/1 16s 33s
kubectl get pod
# NAME READY STATUS RESTARTS AGE
# echo-job-g2bf6 0/1 Completed 0 68s
kubectl logs echo-job
# hello world
apiVersion: batch/v1
kind: Job
metadata:
creationTimestamp: null
name: sleep-job
spec:
# 设置 Pod 运行的超时时间
activeDeadlineSeconds: 60
# 设置 Pod 的失败重试次数
backoffLimit: 2
# Job 完成需要运行多少个 Pod,默认是 1
completions: 4
# 它与 completions 相关,表示允许并发运行的 Pod 数量,避免过多占用资源
parallelism: 2

template:
metadata:
creationTimestamp: null
spec:
containers:
- image: busybox
name: echo-job
resources: {}
# 随机休眠
command:
- sh
- -c
- sleep $(($RANDOM % 10 + 1)) && echo done
restartPolicy: Never
status: {}
kubectl apply -f sleep-job.yaml

kubectl get pod -w
# NAME READY STATUS RESTARTS AGE
# sleep-job-92m4d 0/1 Completed 0 30s
# sleep-job-g8pmj 0/1 Completed 0 15s
# sleep-job-tsncl 0/1 Completed 0 30s
# sleep-job-x4qlp 0/1 Completed 0 15s
# cronjob
kubectl create cj echo-cj --image=busybox --schedule="*/1 * * * *" --dry-run=client -o yaml > echo-cj.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
creationTimestamp: null
name: echo-cj
spec:
jobTemplate:
metadata:
creationTimestamp: null
name: echo-cj
spec:
template:
metadata:
creationTimestamp: null
spec:
containers:
- image: busybox
name: echo-cj
resources: {}
# 补充输出
command: ["/bin/echo"]
args: ["hello", "world"]
restartPolicy: OnFailure
schedule: "*/1 * * * *"
status: {}
kubectl apply -f echo-cj.yaml
kubectl get cj
# NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
# echo-cj */1 * * * * False 1 5s 8s
kubectl get pod
# NAME READY STATUS RESTARTS AGE
# echo-cj-27942326-qng4j 0/1 Completed 0 93s
# echo-cj-27942327-vlrvs 0/1 Completed 0 33s

14 ConfigMap Secret 管理配置信息

kubectl create cm info --from-literal=name=zhao --dry-run=client -o yaml > cm.yml
kubectl apply -f cm.yml
kubectl get cm
kubectl describe cm info
kubectl create secret generic user --from-literal=name=root --dry-run=client -o yaml > secret.yml

# -n 去掉字符串里隐含的换行符
echo -n "root" | base64
kubectl apply -f secret.yml
kubectl get secret
kubectl describe secret user
kubectl explain pod.spec.containers.env.valueFrom
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: ngx
name: ngx
spec:
containers:
- image: nginx:alpine
name: ngx
resources: {}
env:
- name: NAME
valueFrom:
configMapKeyRef:
name: info
key: name
- name: SNAME
valueFrom:
secretKeyRef:
name: user
key: name
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
kubectl apply -f env-pod.yml
kubectl exec -it ngx -- sh

echo $NAME $SNAME
# 以 Volume 的方式使用 ConfigMap/Secret
apiVersion: v1
kind: Pod
metadata:
name: vol-pod
spec:
# Volume 属于 Pod 与 containers 同级
volumes:
- name: cm-vol
configMap:
name: info
- name: sec-vol
secret:
secretName: user
containers:
# 挂载到容器里的某个路径下
- volumeMounts:
- mountPath: /tmp/cm-items
name: cm-vol
- mountPath: /tmp/sec-items
name: sec-vol
image: nginx:alpine
name: ngx
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
vim vol-pod.yml
kubectl apply -f vol-pod.yml
kubectl get pod
kubectl exec -it vol-pod -- sh

cat /tmp/cm-items/name
cat /tmp/sec-items/name
# ConfigMap 和 Secret 都变成了目录的形式,而它们里面的 Key-Value 变成了一个个的文件,而文件名就是 Key。

15 玩转 Kubernetes

image

搭建 WordPress 环境:

# vim mariadb-pod.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: maria-cm
data:
DATABASE: "db"
USER: "wp"
PASSWORD: "123"
ROOT_PASSWORD: "123"

---
apiVersion: v1
kind: Pod
metadata:
name: maria-pod
labels:
app: wordpress
role: database
spec:
containers:
- image: mariadb:10
name: maria
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3306
envFrom:
- prefix: "MARIADB_"
configMapRef:
name: maria-cm
kubectl apply -f mariadb-pod.yml
# 获取 IP 地址需要加上参数 -o wide
kubectl get pod -o wide
# NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
# maria-pod 1/1 Running 0 64s 172.17.0.5 minikube <none> <none>
# vim wp-pod.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: wp-cm
data:
# MariaDB Pod 的 IP
HOST: "172.17.0.5"
USER: "wp"
PASSWORD: "123"
NAME: "db"

---
apiVersion: v1
kind: Pod
metadata:
name: wp-pod
labels:
app: wordpress
role: website
spec:
containers:
- image: wordpress:5
name: wp-pod
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
envFrom:
- prefix: "WORDPRESS_DB_"
configMapRef:
name: wp-cm
kubectl apply -f wp-pod.yml
kubectl get pod -o wide
# NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
# maria-pod 1/1 Running 0 160m 172.17.0.5 minikube <none> <none>
# wp-pod 1/1 Running 0 2m47s 172.17.0.6 minikube <none> <none>
# 本地的 “8080” 映射到 WordPress Pod 的“80”
kubectl port-forward wp-pod 8080:80 &
# Forwarding from 127.0.0.1:8080 -> 80
# Forwarding from [::1]:8080 -> 80
# fg

16 初级篇总结

minikube version
minikube status
minikube start --kubernetes-version=v1.23.3
minikube node list

kubectl version
kubectl run ngx --image=nginx:alpine

# apiserver 等核心组件是在 kube-system 名字空间
kubectl get pod -n kube-system

kubectl api-resources
kubectl explain pod.metadata

export out="--dry-run=client -o yaml"
kubectl run ngx --image=nginx:alpine $out > pod.yml

kubectl apply -f ngx-pod.yml
kubectl get pod
kubectl logs ngx-pod
kubectl exec -it ngx-pod -- sh
kubectl delete -f ngx-pod

kubectl create job echo-job --image=busybox $out
kubectl apply -f job.yml
kubectl get job
kubectl get pod
kubectl logs echo-job-l52l7

kubectl create cj echo-cj --image=busybox --schedule="* * * * *" $out
kubectl apply -f cronjob.yml
kubectl get cj
kubectl get pod

kubectl create cm info --from-literal=k=v $out
kubectl get cm
kubectl describe cm info

kubectl create secret generic user --from-literal=name=root $out
kubectl get secret
kubectl describe secret user

echo cm9vdA== | base64 -d

References

– EOF –