一、前置知识点

目前生产部署Kubernetes集群主要有三种种方式:

minikube

minikube是一个工具,可以在本地快速运行一个单节点的kubernetes,仅用于尝试K8S或日常开发的测试环境使用。

kubeadm

kubeadm是一个K8S部署工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。

官方地址:Kubeadm | Kubernetes

二进制包

从github下载发行版的二进制包,手动部署每个组件,组成Kubernetes集群。

kubeadm降低了部署门槛,但是屏蔽了很多细节,遇到问题很难排查。如果想更容易可控,推荐使用二进制包部署Kubernetes集群,虽然手动部署麻烦点,期间可以学习很多工作原理,也利于后期维护。

部署方式:

  • 一主多从:测试环境、练习环境。
  • 多主多从:生产环境推荐使用。

二、kubeadm部署方式介绍

kubeadm 是官方社区推出的一个用于快速部署kubernetes集群的工具,这个工具能通过两条指令完成一个kubernetes集群的部署:

  • kubeadm init:创建一个Master节点。
  • kubeadm join <Master节点的IP和端口>:将Node节点加入到当前集群中。

三、安装要求

在开始之前,部署Kubernetes集群的机器需要满足以下几个条件:

  • 一台或多台机器,操作系统CentOS7.x-86_x64(我自己复现的环境用的CentOS 8)
  • 硬件配置:2GB 或更多RAM,2 个CPU 或更多CPU,硬盘30GB 或更多
  • 集群中所有机器之间网络互通
  • 可以访问外网,需要拉取镜像
  • 禁止swap 分区

四、最终目标

  • 在所有节点上安装Docker和kubeadm
  • 部署Kubernetes Master
  • 部署容器网络插件
  • 部署Kubernetes Node,将节点加入Kubernetes集群中
  • 部署Dashboard Web页面,可视化查看Kubernetes资源

五、准备环境

角色 IP地址 组件
k8s-master01 192.168.9.10 docker,kubectl,kubeadm,kubelet
k8s-node01 192.168.9.20 docker,kubectl,kubeadm,kubelet
k8s-node02 192.168.9.30 docker,kubectl,kubeadm,kubelet

备注:学习环境为一个Master,两个Node节点。

六、环境初始化

6.1 设置系统主机名以及Hosts文件的相互解析

  • 修改主机名
hostnamectl set-hostname k8s-master01 && bash
hostnamectl set-hostname k8s-node01 && bash
hostnamectl set-hostname k8s-node02 && bash
  • 修改Master节点的/etc/hosts
192.168.9.10     k8s-master01
192.168.9.20 k8s-node01
192.168.9.30 k8s-node02
  • 拷贝到Node节点
scp /etc/hosts root@192.168.9.20:/etc/hosts
scp /etc/hosts root@192.168.9.30:/etc/hosts
  • 测试连通性
ping k8s-master01
ping k8s-node01
ping k8s-node02

6.2 安装依赖文件(所有节点)

yum install -y conntrack chrony ipvsadm ipset jq iptables curl sysstat libseccomp wget vim net-tools git

备注:在CentOS 8里面,用chrony服务代替了NTP。

6.3 禁用iptables和firewalld服务(所有节点)

systemctl stop firewalld
systemctl disable firewalld systemctl stop iptables
systemctl disable iptables

6.4 禁用Swap分区和SELinux(所有节点)

# 1.禁用swap分区
swapoff -a && sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 2.禁用SELinux
setenforce 0 && sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

6.5 调整内核参数,对于K8S(所有节点)

  • 修改Linux的内核参数/etc/sysctl.d/kubernetes.conf
# 修改Linux的内核参数,添加网桥过滤和地址转发功能
# 编辑/etc/sysctl.d/kubernetes.conf文件,添加如下配置:
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1 # 重新加载配置
sysctl -p # 加载网桥过滤模块
modprobe br_netfilter # 查看网桥过滤模块的是否加载成功
lsmod | grep br_netfilter
  • 配置ipvs功能

在kubernetes中service有两种代理模型,一种是基于iptables的,一种是基于ipvs的。

两者比较的话,ipvs的性能明显要高一些,但是如果要使用它,需要手动载入ipvs模块。

# 1.安装ipset和ipvsadm
yum install ipset ipvsadm -y # 2.添加需要加载的模块写入脚本
cat <<EOF > /etc/sysconfig/modules/ipvs.modules
#! /bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF # 3.为脚本文件添加执行权限
chmod +x /etc/sysconfig/modules/ipvs.modules # 4.执行脚本文件
/bin/bash /etc/sysconfig/modules/ipvs.modules # 5.查看对应的模块是否加载成功
lsmod | grep -e ip_vs -e nf_conntrack_ipv4

6.6 调整系统时区(所有节点,选做)

# 设置系统时区为 中国/上海
timedatectl set-timezone Asia/Shanghai
# 将当前的 UTC 时间写入硬件时钟
timedatectl set-local-rtc 0
# 重启依赖于系统时间的服务
systemctl restart rsyslog
systemctl restart crond

6.7 设置rsyslogd和systemd journald(所有节点,选做)

# 持久化保存日志的目录
mkdir /var/log/journal
mkdir /etc/systemd/journald.conf.d
cat > /etc/systemd/journald.conf.d/99-prophet.conf <<EOF
[Journal]
# 持久化保存到磁盘
Storage=persistent # 压缩历史日志
Compress=yes SyncIntervalSec=5m
RateLimitInterval=30s
RateLimitBurst=1000 # 最大占用空间 10G
SystemMaxUse=10G # 单日志文件最大 200M
SystemMaxFileSize=200M # 日志保存时间 2 周
MaxRetentionSec=2week # 不将日志转发到 syslog
ForwardToSyslog=no
EOF systemctl restart systemd-journald

6.8 安装Docker(所有节点)

# 1.切换镜像源
wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo # 2.查看当前镜像源中支持的docker版本
yum list docker-ce --showduplicates # 3.安装特定版本的docker-ce
# 可以指定 --setopt=obsoletes=0,否则yum会自动安装更高的版本
yum install docker-ce -y # 4.添加一个配置文件
# Docker在默认情况下使用的Cgroup Driver为cgroupfs,而kubernetes推荐使用systemd来代替cgroupfs
mkdir /etc/docker
cat <<EOF > /etc/docker/daemon.json
{
"exec-opts":["native.cgroupdriver=systemd"],
"registry-mirrors":["https://gq8usroj.mirror.aliyuncs.com"]
}
EOF # 5.启动Docker
systemctl restart docker
systemctl enable docker # 6.检查docker状态和版本
docker version

6.9 安装kubenetes组件(所有节点)

# 由于kubernetes的镜像源在国外,速度比较慢,这里切换成国内的镜像源
# 编辑/etc/yum.repos.d/kubernetes.repo,添加下面的配置
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF # 安装kubeadm、kubelet和kubectl
yum install kubeadm kubelet kubectl # 配置kubelet的cgroup
# 编辑/etc/sysconfig/kubelet,添加下面的配置
KUBELET_CGROUP_ARGS="--cgroup-driver=systemd"
KUBE_PROXY_MODE="ipvs" # 设置kubelet开机自启
systemctl enable kubelet

七、部署Kubernetes集群

7.1 准备集群镜像

使用kubeadm config images list查看所需要的镜像:

[root@k8s-master01 ~]# kubeadm config images list
k8s.gcr.io/kube-apiserver:v1.23.1
k8s.gcr.io/kube-controller-manager:v1.23.1
k8s.gcr.io/kube-scheduler:v1.23.1
k8s.gcr.io/kube-proxy:v1.23.1
k8s.gcr.io/pause:3.6
k8s.gcr.io/etcd:3.5.1-0
k8s.gcr.io/coredns/coredns:v1.8.6
# 在安装Kubernetes集群之前,必须要提前准备号集群需要的镜像,所需镜像可以通过下面命令查看
kubeadm config images list # 下载镜像
# 此镜像在kubernetes的仓库中,由于网络原因,无法连接,下面提供了一种替代方案
images=(
kube-apiserver:v1.23.1
kube-controller-manager:v1.23.1
kube-scheduler:v1.23.1
kube-proxy:v1.23.1
pause:3.6
etcd:3.5.1-0
coredns:v1.8.6
) # 拉取所需要的镜像
for imageName in ${images[@]};
do
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
done # 重新给镜像打标签,如果下面kube init的时候指定仓库,可以不需要重新tag
for imageName in ${images[@]};
do
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName
done # 删除掉原名字的镜像
for imageName in ${images[@]};
do
docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
done

效果如下:

[root@k8s-master01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
k8s.gcr.io/kube-apiserver v1.23.1 b6d7abedde39 2 weeks ago 135MB
k8s.gcr.io/kube-proxy v1.23.1 b46c42588d51 2 weeks ago 112MB
k8s.gcr.io/kube-scheduler v1.23.1 71d575efe628 2 weeks ago 53.5MB
k8s.gcr.io/kube-controller-manager v1.23.1 f51846a4fd28 2 weeks ago 125MB
k8s.gcr.io/etcd 3.5.1-0 25f8c7f3da61 8 weeks ago 293MB
k8s.gcr.io/coredns v1.8.6 a4ca41631cc7 2 months ago 46.8MB
k8s.gcr.io/pause 3.6 6270bb605e12 4 months ago 683kB

7.2 集群初始化

下面开始对集群进行初始化,并将node节点加入到集群中

下面的操作只需要在master节点上执行即可

这一步很关键,由于kubeadm 默认从官网k8s.gcr.io下载所需镜像,国内无法访问,因此需要通过--image-repository=指定阿里云镜像仓库地址。

# 创建集群
kubeadm init \
--kubernetes-version=v1.23.1 \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12 \
--image-repository registry.aliyuncs.com/google_containers \
--apiserver-advertise-address=192.168.9.10 # 创建必要文件
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME /.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

集群初始化成功后返回如下信息:

[root@k8s-master01 ~]# kubeadm init \
> --kubernetes-version=v1.23.1 \
> --pod-network-cidr=10.244.0.0/16 \
> --service-cidr=10.96.0.0/12 \
> --image-repository registry.aliyuncs.com/google_containers \
> --apiserver-advertise-address=192.168.9.10
[init] Using Kubernetes version: v1.23.1
[preflight] Running pre-flight checks
[WARNING FileExisting-tc]: tc not found in system path
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull' [certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [k8s-master01 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.9.10]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [k8s-master01 localhost] and IPs [192.168.9.10 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [k8s-master01 localhost] and IPs [192.168.9.10 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 7.503475 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.23" in namespace kube-system with the configuration for the kubelets in the cluster
NOTE: The "kubelet-config-1.23" naming of the kubelet ConfigMap is deprecated. Once the UnversionedKubeletConfigMap feature gate graduates to Beta the default name will become just "kubelet-config". Kubeadm upgrade will handle this transition transparently.
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node k8s-master01 as control-plane by adding the labels: [node-role.kubernetes.io/master(deprecated) node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node k8s-master01 as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: 9xx9un.p2x8icgh1wxo6y45
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config Alternatively, if you are the root user, you can run: export KUBECONFIG=/etc/kubernetes/admin.conf You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join 192.168.9.10:6443 --token 9xx9un.p2x8icgh1wxo6y45 \
--discovery-token-ca-cert-hash sha256:797daf093fb0c4bbde161107bf297f858f76fe368ae66e789d3bd331ecc51480

然后需要在Master节点上运行如下:

#To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

然后可以看到如下效果:

[root@k8s-master01 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master01 NotReady control-plane,master 5m9s v1.23.1

下面的操作只需要在node节点上执行即可

然后,将Node节点加入集群中

# 在node节点中输入
kubeadm join 192.168.9.10:6443 --token 9xx9un.p2x8icgh1wxo6y45 \
--discovery-token-ca-cert-hash sha256:797daf093fb0c4bbde161107bf297f858f76fe368ae66e789d3bd331ecc51480

然后在Master节点看到如下效果:

[root@k8s-master01 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master01 NotReady control-plane,master 9m43s v1.23.1
k8s-node01 NotReady <none> 11s v1.23.1
k8s-node02 NotReady <none> 15s v1.23.1

但是此时,STATUS是NotReady,这是因为还没有安装网络插件。

7.3 安装网络插件

Kubernetes支持多种网络插件,比如flannel、calico、canal等,任选一种使用即可,本次选择flannel。

下面操作依旧只在master节点执行即可,插件使用的是DaemonSet的控制器,它会在每一个节点上都运行。

# 获取fannel的配置文件
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# 上面的不行的话用这个
# https://raw.fastgit.org/coreos/flannel/master/Documentation/kube-flannel.yml # 修改文件中quay.io仓库为quay-mirror.qiniu.com # 使用配置文件启动fannel
kubectl apply -f kube-flannel.yml
# 稍等片刻,再次查看集群节点的状态
[root@k8s-master01 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master01 Ready control-plane,master 24m v1.23.1
k8s-node01 Ready <none> 14m v1.23.1
k8s-node02 Ready <none> 14m v1.23.1

至此,Kubernetes集群环境搭建完成。

八、测试Kubernetes集群

接下来在Kubernetes集群中部署一个nginx程序,测试下集群是否在正常工作。

# 部署nginx
kubectl create deployment nginx --image=nginx:latest
# 暴露端口
kubectl expose deployment nginx --port=80 --type=NodePort
# 查看服务状态
[root@k8s-master01 ~]# kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/nginx-7c658794b9-9xwg7 0/1 ContainerCreating 0 17s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 46m
service/nginx NodePort 10.97.244.84 <none> 80:31182/TCP 11s

上面的svc = service

然后在电脑上测试访问31182端口

这样子,就完成了测试!

[云原生]Kubernetes - 集群搭建(第2章)的更多相关文章

  1. 云原生 PostgreSQL 集群 - PGO:来自 Crunchy Data 的 Postgres Operator

    使用 PGO 在 Kubernetes 上运行 Cloud Native PostgreSQL:来自 Crunchy Data 的 Postgres Operator! Cloud Native Po ...

  2. Kubernetes集群搭建(详细)

    kubernetes集群搭建(kubeadm方式) kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具.这个工具能通过两条指令完成一个kubernetes集群的部署: # 创 ...

  3. Kubernetes集群搭建之企业级环境中基于Harbor搭建自己的私有仓库

    搭建背景 企业环境中使用Docker环境,一般出于安全考虑,业务使用的镜像一般不会从第三方公共仓库下载.那么就要引出今天的主题 企业级环境中基于Harbor搭建自己的安全认证仓库 介绍 名称:Harb ...

  4. kubernetes集群搭建(2):docker私有仓库

    kubernetes集群搭建(1):环境准备 中各节点已经安装好了docker,请确认docker已启动并正常运行 1.通过命令启动私库 docker run -d -p 5000:5000 --pr ...

  5. Centos 7 kubernetes集群搭建

    一.环境准备 Kubernetes支持在物理服务器或虚拟机中运行,本次使用虚拟机准备测试环境,硬件配置信息如表所示: IP地址 节点角色 CPU Memory Hostname 磁盘 192.168. ...

  6. Kubernetes集群搭建之系统初始化配置篇

    Kubernetes的几种部署方式 1. minikube Minikube是一个工具,可以在本地快速运行一个单点的Kubernetes,尝试Kubernetes或日常开发的用户使用.不能用于生产环境 ...

  7. Kubernetes集群搭建 ver1.20.5

    目录 部署方式 1. 基础环境准备 1.1 基础初始化 1.2 安装docker 2. 部署harbor及haproxy高可用反向代理 2.1 镜像加速配置 2.2 高可用master可配置 3. 初 ...

  8. Kubernetes集群搭建过程中遇到的问题

    1. 创建Nginx Pod过程中报如下错误: #kubectlcreate -f nginx-pod.yaml Error from server: error when creating &quo ...

  9. Docker 与 K8S学习笔记(二十三)—— Kubernetes集群搭建

    小伙伴们,好久不见,这几个月实在太忙,所以一直没有更新,今天刚好有空,咱们继续k8s的学习,由于我们后面需要深入学习Pod的调度,所以我们原先使用MiniKube搭建的实验环境就不能满足我们的需求了, ...

随机推荐

  1. web必知,多终端适配

    导读 移动端适配,是我们在开发中经常会遇到的,这里面可能会遇到非常多的问题: 1px问题 UI图完美适配方案 iPhoneX适配方案 横屏适配 高清屏图片模糊问题 ... 上面这些问题可能我们在开发中 ...

  2. 转 关于HttpClient,HttpURLConnection,OkHttp的用法

    转自:https://www.cnblogs.com/zp-uestc/p/10371012.html 1 HttpClient入门实例 1.1发送get请求 1 2 3 4 5 6 7 8 9 10 ...

  3. js处理title超长问题

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...

  4. React 传值 组件传值 之间的关系

    react 组件相互之间的传值: 传值分父级组件传值给子组件   子组件传值给父组件    平级组件.没有嵌套的组件相互传值 1.父组件向子组件传值 父组件通过属性的形式来向子组件传值,子组件通过pr ...

  5. Properties类继承HashTable类,一般用来给程序配置属性文件。

    package com.itcast.demo04.Prop;import jdk.internal.util.xml.impl.ReaderUTF8;import sun.nio.cs.UTF_32 ...

  6. Centos7源码部署Redis3.2.9

    目录 一.环境准备 二.安装 三.测试 四.编写启动脚本 一.环境准备 [Redis-Server] 主机名 = host-1 系统 = centos-7.3 地址 = 1.1.1.1 软件 = re ...

  7. Latex-安装_第一天

    LaTex安装 Windows 小知识: \(Tex\)来源technology,希腊词根是\(tex\),Latex应该读成"拉泰赫". https://miktex.org/ ...

  8. uwsgi nginx与django之间的关系以及各自的作用

    首先要明确几个概念及其作用(注意大小写的区别): WSGI uWSGI uwsgi Nginx WSGI 是一种协议,不是任何包不是任何服务器,就和 TCP 协议一样.它定义了 Web 服务器和 We ...

  9. CF1106A Lunar New Year and Cross Counting 题解

    Content 试求出在一个 \(n\times n\) 的地图 \(M\) 中,满足 \(1\leqslant i,j\leqslant n\) 且 \(M_{i,j}=M_{i+1,j+1}=M_ ...

  10. Java 中接口和抽象类的 7 大区别!

    本文已收录<Java常见面试题>:https://gitee.com/mydb/interview ​ Java 是一门面向对象的编程语言,面向对象的编程语言有四大特征:抽象.封装.继承和 ...