쿠베르네트스 녹 방지제

쿠베르네트스(Kubernetes), 특히 controllers를 독학하기 위해 나는 예전developed one in Java에 다녔다.이번 주에 나는 내가 한 똑같은 절차에 따라 에서 똑같은 일을 하기로 결정했다.

지도 원칙


그 지도 원칙은 Kubernetes 컨트롤러를 만들어 기중기의 생명 주기를 감시하고 그 안에 사이드카를 주입하는 것이다.쿠베르네트스가 기중기를 조정할 때 컨트롤러가 옆차를 조정한다.전자를 삭제할 때도 후자를 삭제한다.
나는 상자를 열면 바로 사용할 수 있다는 것을 알고 있지만, 이것은 매우 좋은 학습 연습이다.그래서 저는 다음과 같은 몇 가지 절차를 통해 개발을 진행했습니다.
  • 콘솔에 POD 목록을 인쇄합니다.코드가 실행되면 프로세스가 정지됩니다.쿠베르네트스는 기중기가 실패했다고 생각한다;그것은 그것을 죽이고 새로운 것을 안배할 것이다.씻고 반복.
  • 프로그램을 순환시켜 종료하지 않도록 한다
  • 순환이 아닌 전용 탐지기를 사용하여 생명주기 이벤트의 알림을 수신한다
  • 실제 사이드카
  • 를 스케줄링하여 로그 기록을 대체한다
    최초의 자바 프로젝트는 더 많은 절차를 사용했지만 본문과는 무관합니다.

    준입 제어원 프로젝트 작성


    이 프로젝트는 내 인생의 첫 번째 프로젝트이다. 나는 반드시 처음부터 프로젝트를 세워야 한다.내 이전의 모든 일은 아니면used an existing project이었다.
    사용cargo:
    cargo new rust-operator     # 1
    
  • 폴더를 생성하고 새 뼈대 프로젝트를 작성합니다.또는 기존 폴더rust-operator에서 사용할 수 있습니다.
  • 원래 구조는 다음과 같습니다.
    rust-operator/
    ├── Cargo.toml
    └── src
        └── main.rs
    
    다음을 포함합니다.
    [package]
    name = "rust-operator"                                # 1
    version = "0.1.0"
    authors = ["Nicolas Frankel <[email protected]>"]    # 2
    edition = "2018"                                      # 3
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]                                        # 4
    
  • 소포 명칭
  • 작성자 글로벌 Git 구성
  • 최신
  • 빈 의존항 목록에서 시작
  • fn main() {
        println!("Hello, world!");
    }
    
    이제 다음을 수행할 수 있습니다.

  • 빌드 패키지
    cargo b
    

  • 그리고 그것을 실행합니다
    target/debug/rust-operator
    
  • 예상대로 출력됩니다.
    Hello, world!
    

    제공된 템플릿 복사 로그인 중


    우선, 우리는 로그 레벨 (디버깅, 정보 등) 을 가진 라이브러리로 상자를 열 때 사용하는 로그 매크로를 대체할 것입니다.
    몇 가지 연구를 통해 나는 판자 상자를 발견했다.

    log4rs is a highly configurable logging framework modeled after Java's Logback and log4j libraries.


    Rust library 생태계에 대한 이해가 부족한 점을 감안하여 저는 cargo init를 선택했습니다. 왜냐하면 디자인이 Log4J와 유사하기 때문입니다.log4rs를 사용하려면 다음 두 가지 종속성을 추가해야 합니다.
    [dependencies]
    log4rs = "1.0.0"        # 1
    log = "0.4.14"          # 2
    
  • log4rs
  • 추가
  • log4rs는 경량급 로그 외관으로 log를 실현
  • 으로 사용한다
    이제 소스 파일의 로그 매크로를 대체할 수 있습니다.
    use log::info;
    use log4rs;
    
    fn main() {
        log4rs::init_file("log4rs.yml", Default::default()).unwrap();      // 1
        info!("Hello, world!");                                            // 2
    }
    
  • 초기화log4rs, 필요log4rs 파일
  • 올바른 로그
  • 현재 바이너리 파일을 실행하면 로그를 출력할 수도 있고, 출력할 수도 있습니다. 이것은 log4rs.yml 파일의 내용에 달려 있습니다.내 견본으로 인쇄하기
    2021-07-05T16:26:16.041150+02:00 INFO rust_operator - Hello, world!
    

    log4rs Rust에서 Kubernetes API 호출


    일지 기록에 관해서 나는 반드시 도서관을 수색해야 한다.나의 기준은 매우 간단하다. 너무 저급하지 마라. 그래서 나는 HTTP 호출을 처리할 필요가 없지만, 너무 고급스럽지 않다. 그래서 나는 어떤 일도 할 필요가 없다.마지막으로 저는 :

    Crate for interacting with the Kubernetes API

    This crate includes the tools for manipulating Kubernetes resources as well as keeping track of those resources as they change over time


    또한 개방형 API 사양에서 생성된 녹 자국을 결합해야 합니다.
    [dependencies]
    kube = "0.52.0"
    k8s-openapi = { version = "0.11.0", default-features = false,
                                        features = ["v1_19"] }     # 1
    
  • 라이브러리는 Kubernetes API 버전당 하나의 기능을 사용합니다.그룹 버전과 관련된 버전을 설정합니다.
  • API의 입구 점은 log4rs.yml입니다.
    kube Client를 얻으려면 가장 간단한 방법Client을 사용해야 한다.Java 코드와 같이

    Will use Config::infer to try in-cluster environment variables first, then fallback to the local kubeconfig.

    Will fail if neither configuration could be loaded.


    비동기 호출


    똑똑한 독자들은 위의 그림Client::try_default()의 판에 박힌 인상을 이미 알아차렸을 것이다.각각 <<async>> 호출은 사실상 비동기적이다.Rust는 Clienttrait, Future/async 문법과 await 판자 상자를 통해 비동기성을 지원한다.다음 중 하나를 시도해 보겠습니다futures:
    fn main() {
        Client::try_default();
    }
    
    코드 세그먼트는 컴파일되지만 경고가 출력됩니다.
    warning: unused implementer of `futures::Future` that must be used
      --> src/main.rs:14:5
       |
    14 |     Client::try_default();
       |     ^^^^^^^^^^^^^^^^^^^^^^
       |
       = note: `#[warn(unused_must_use)]` on by default
       = note: futures do nothing unless you `.await` or poll them
    
    컴파일러가 주의한 바와 같이 aClient 자체는 아무것도 하지 않는다.그것을 사용하려면, 우리는 반드시 Future 그것을 사용해야 한다.
    fn main() {
        Client::try_default().await;
    }
    
    이제 코드가 실패했습니다.
    error[E0728]: `await` is only allowed inside `async` functions and blocks
      --> src/main.rs:14:18
       |
    12 | fn main() {
       |    ---- this is not `async`
    13 |     log4rs::init_file("log4rs.yml", Default::default()).unwrap();
    14 |     let client = Client::try_default().await;
       |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks
    
    교훈은 다른 await 함수(와 블록)에서만 호출할 수 있음async 함수입니다.async 아니main.가장 간단한 방법은 테이프 인데, 이것은 비동기 프로그래밍을 위한 판자 상자이다.Tokio는 async 비동기적인 매크로를 제공했다.
    #[tokio::main]
    async fn main() {
        Client::try_default().await;
    }
    
    마지막 단계는 열기main에 포함된 Client입니다.
    #[tokio::main]
    async fn main() {
        let client = Client::try_default().await.unwrap();
    }
    

    동경 콩꼬투리를 열거하다


    이 점에서, 우리는 콩꼬투리를 열거하는 것과 같은 Kubernetes 집단과 상호작용을 할 수 있는 Result을 얻었다.입구점은 범형Client 유형이고 그 중에서Api<K>는 우리가 흥미를 느끼는 Kubernetes 대상이다.

    이제 작은 것부터 콩꼬투리를 열거해 봅시다.KApi::list() 파라미터가 필요합니다. 이 파라미터는 실현ListParams됩니다.
    따라서 코드는 매우 간단합니다.
    #[tokio::main]
    async fn main() {
        let api: Api<Pod> = Api::namespaced(client, "kube-system");   <1>
        api.list(&ListParams::default())
            .await
            .unwrap()
            .items
            .iter()
            .map(|pod| pod.name())
            .for_each(|name| info!("{}", name));
    }
    
  • 명칭공간Default의 모든pods대상
  • 코드가 예상대로 작동합니다!

    콩꼬투리를 보다


    다음 단계는 크레인을 관찰하는 방법으로 옮기는 것이다.이를 위해, 우리는 매번 변경할 때마다 통지를 받을 수 있도록 손목시계를 등록해야 한다.네, 그게 문제예요.API는 매우 간단하지만 코드 자체는 결코 간단하지 않다.
    #[tokio::main]
    async fn main() {
        let client = Client::try_default().await.unwrap();
        let api: Api<Pod> = Api::namespaced(client, "kube-system");
        let mut stream = api.watch(&ListParams::default(), "0")        <1>
          .await?                                                      <2>
          .boxed();                                                    <3>
        while let Some(event) = stream
          .try_next()                                                  <4>
          .await? {
            match event {
                _ => {}
            };
        }
    }
    
  • 반환kube-system 호출watch()
  • 기본 확보Result 또는 반환 대기 Stream
  • 상자Error.
    본문을 쓸 때, 나는 찌르는 것에 대한 이해가 제로이기 때문에, 그것이 필요하고 효과가 있다고 가정한다.
  • Stream의 다음 품목을 Stream로 패키지
  • 기본 획득Result 또는 반환WatchEvent
  • 그러나 컴파일이 실패한 이유Error:
    error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `Try`)
      --> src/main.rs:12:22
       |
    8  |   async fn main() {
       |  _________________-
     9 | |     let client = Client::try_default().await.unwrap();
    10 | |     let api: Api<Pod> = Api::namespaced(client, "kube-system");
    11 | |     let mut stream = api.watch(&ListParams::default(), "0").await?.boxed();
       | |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in an async block that returns `()`
    12 | |     while let Some(event) = stream.try_next().await? {
    13 | |     }
    14 | | }
       | |_- this function should return `Result` or `Option` to accept `?`
       |
       = help: the trait `Try` is not implemented for `()`
       = note: required by `from_error`
    
    기억해라. await? 일반적인 값을 포함하거나, 실패를 포함하거나, 보통 Result 이다.Error 연산자는 ?의 바로 가기입니다. 다음을 수행할 수 있습니다.
  • 일반값을 확장하여 계속

  • 현재 함수에서 실패
  • 를 반환합니다.
    위의 코드 세그먼트에서 Result 함수는 어떤 반환 형식도 정의하지 않았습니다.컴파일 문제를 해결하려면 다음을 추가해야 합니다.
    #[tokio::main]
    async fn main() -> Result<(), Error> {                                        # 1
        let client = Client::try_default().await.unwrap();
        let api: Api<Pod> = Api::namespaced(client, "kube-system");
        let mut stream = api.watch(&ListParams::default(), "0").await?.boxed();
        while let Some(event) = stream.try_next().await? {
            match event {
                WatchEvent::Added(pod) => info!("ADDED: {}", pod.name()),
                WatchEvent::Modified(pod) => info!("UPDATED: {}", pod.name()),
                WatchEvent::Deleted(pod) => info!("DELETED: {}", pod.name()),
                WatchEvent::Error(e) => error!("ERROR: {} {} ({})", e.code, e.message, e.status),
                _ => {}
            };
        }
        Ok(())                                                                    # 2
    }
    
  • 공백 정의 main() 반환
  • 성공적으로 컴파일하는 데 필요한
  • 컨트롤러 코드의 나머지 부분은 매우 간단하다. 매번 기중기를 추가할 때, 만약 그것이 옆차가 아니라면, 기중기에 옆차를 추가하고, 후자를 소유자로 한다.

    컨테이너화 컨트롤러


    코드를 컨테이너화하기 위해 다단계 빌드를 사용합니다.
    FROM ekidd/rust-musl-builder:1.51.0 as build
    
    WORKDIR /app
    
    COPY src src
    COPY Cargo.lock .
    COPY Cargo.toml .
    
    RUN cargo build --release                       # 1
    
    FROM scratch                                    # 2
    
    WORKDIR /app
    
    COPY --from=build /app/target/x86_64-unknown-linux-musl/release/rust-operator /app
    COPY log4rs.yml .                               # 3
    
    CMD ["./rust-operator"]
    
  • 바이너리 파일을 릴리스로 제작
  • 부터Result가능한 최소 크기
  • 로그 프로필 잊지 마세요
  • 응, 마지막 사이즈는 충분해.
    REPOSITORY           TAG           IMAGE ID             CREATED             SIZE
    rust-operator        latest        5cac942d46a0         1 hour ago          18MB
    

    결론


    이 문서에서 Kubernetes 컨트롤러를 만드는 방법을 설명합니다.Rust의 의미는 다음과 같습니다.
  • 구축 프로젝트
  • 필요한 소량의 의존 항목 추가
  • 인코딩
  • 구성 다단계 구축
  • 즐겨라!
  • 읽을 수 있는 코드를 제외하고 가장 중요한 장점은 Rust 컴파일러가 보안 코드를 생성하는 모든 힌트를 만드는 것이다.
    이 글의 전체 소스 코드는 Github에서 찾을 수 있습니다.

    / 아자바지크



    한 걸음 더 나아가 말하면:
  • rust-operator
  • The Cargo book
  • Where does Cargo get the name and email from when creating a project?
  • log4rs crate
  • kube crate
  • Asynchronous Programming in Rust
  • 최초 발표는 2021년 7월 11일The ? operator for easier error handling

    좋은 웹페이지 즐겨찾기