컨테이너 작업을 위한 권장사항

Last reviewed 2023-02-28 UTC

이 문서에서는 컨테이너 작업을 쉽게 하기 위한 일련의 권장사항을 설명합니다. 이러한 권장사항은 보안부터 모니터링, 로깅에 이르기까지 폭넓은 주제를 다룹니다. 목적은 Google Kubernetes Engine 및 컨테이너에서 전반적으로 애플리케이션을 보다 쉽게 실행하는 데 있습니다. 여기서 설명하는 권장사항의 상당수는 클라우드 기반 애플리케이션 빌드를 위한 유용한 리소스인 12요소 방법론을 기반으로 합니다.

이러한 권장사항의 중요도는 각기 다릅니다. 예를 들어 권장사항 중 일부를 적용하지 않고 성공적으로 프로덕션 워크로드를 실행할 수 있더라도 그 외의 권장사항은 필수적일 수 있습니다. 특히 보안 관련 권장사항의 중요도는 주관적입니다. 이러한 권장사항을 구현할지 여부는 각자의 환경과 제약 조건에 따라 달라집니다.

이 문서를 최대한 활용하려면 Docker와 Kubernetes에 대한 어느 정도의 지식이 필요합니다. 여기서 설명하는 일부 권장사항은 Windows 컨테이너에도 적용되지만 대부분은 Linux 컨테이너 작업을 전제로 합니다. 컨테이너 구축에 대한 도움말은 컨테이너 빌드를 위한 권장사항을 참조하세요.

컨테이너의 네이티브 로깅 메커니즘 사용

중요도: 높음

애플리케이션 관리의 필수적인 부분인 로그에는 애플리케이션에서 발생하는 이벤트에 대한 중요한 정보가 포함됩니다. Docker 및 Kubernetes는 용이한 로그 관리를 위한 기능을 제공합니다.

일반적인 서버에서는 대부분 특정 파일에 로그를 쓰고, 디스크가 꽉 차지 않도록 로그 로테이션을 처리해야 합니다. 고급 로깅 시스템을 사용하는 경우 이러한 로그를 원격 서버로 전달하여 중앙화할 수 있습니다.

컨테이너에서는 stdoutstderr에 로그를 쓸 수 있으므로 표준화된 방법으로 손쉽게 로그를 관리할 수 있습니다. Docker는 이러한 로그 라인을 캡처하고 사용자가 docker logs 명령어를 사용하여 로그에 액세스할 수 있도록 합니다. 애플리케이션 개발자는 고급 로깅 메커니즘을 구현할 필요가 없습니다. 대신 네이티브 로깅 메커니즘을 사용하세요.

플랫폼 운영자는 로그를 중앙화하고 검색 가능하도록 하기 위한 시스템을 제공해야 합니다. GKE에서 이 서비스는 Fluent BitCloud Logging을 통해 제공됩니다. GKE 클러스터 마스터 버전에 따라 Fluentd 또는 Fluent Bit가 로그를 수집하는 데 사용됩니다. GKE 1.17부터 Fluentbit 기반 에이전트를 사용하여 로그가 수집됩니다. GKE 1.17 이전의 버전을 사용하는 GKE 클러스터는 Fluentd 기반 에이전트를 사용합니다. 다른 Kubernetes 배포의 일반적인 방법에는 EFK(Elasticsearch, Fluentd, Kibana) 스택을 사용하는 방법이 포함됩니다.

Kubernetes의 기본 로그 관리 시스템 다이어그램
그림 1. Kubernetes의 일반적인 로그 관리 시스템 다이어그램

JSON 로그

대부분의 로그 관리 시스템은 사실상 시간 색인 생성 문서를 저장하는 시계열 데이터베이스입니다. 이러한 문서는 일반적으로 JSON 형식으로 제공됩니다. Cloud Logging 및 EFK에서는 하나의 로그 라인이 일부 메타데이터(포드, 컨테이너, 노드 등에 대한 정보)와 함께 문서로 저장됩니다.

다양한 필드의 JSON 형식으로 직접 로깅하여 이 동작을 활용할 수 있습니다. 그런 다음 이러한 필드를 기반으로 더 효과적으로 로그를 검색할 수 있습니다.

예를 들어 다음 로그를 JSON 형식으로 변환하는 경우를 가정해 보겠습니다.

[2018-01-01 01:01:01] foo - WARNING - foo.bar - There is something wrong.

변환된 로그는 다음과 같습니다.

{
  "date": "2018-01-01 01:01:01",
  "component": "foo",
  "subcomponent": "foo.bar",
  "level": "WARNING",
  "message": "There is something wrong."
}

이 변환을 통해 로그에서 모든 WARNING 레벨 로그를 검색하거나 하위 구성요소 foo.bar의 모든 로그를 손쉽게 검색할 수 있습니다.

JSON 형식 로그를 작성하기로 한 경우 올바르게 파싱되도록 하려면 각 이벤트를 하나의 라인에 써야 한다는 점에 유의하세요. 실제 로그는 다음과 같은 형태가 됩니다.

{"date":"2018-01-01 01:01:01","component":"foo","subcomponent":"foo.bar","level": "WARNING","message": "There is something wrong."}

여기서 볼 수 있듯이 결과는 일반적인 로그 라인에 비해 가독성이 훨씬 떨어집니다. 이 방법을 사용하려면 팀에서 수동 로그 검사에 많이 의존하지 않는지 확인해야 합니다.

로그 애그리게이터 사이드카 패턴

일부 애플리케이션(예: Tomcat)은 stdoutstderr에 로그를 작성하도록 구성하기가 쉽지 않습니다. 이러한 애플리케이션은 디스크의 다양한 로그 파일에 쓰므로 Kubernetes에서 이를 처리하는 최선의 방법은 로깅에 사이드카 패턴을 사용하는 것입니다. 사이드카는 애플리케이션과 같은 포드에서 실행되는 작은 컨테이너입니다. 사이드카에 대한 자세한 내용은 공식 Kubernetes 문서를 참조하세요.

이 솔루션에서는 GitHub의 이 YAML 예시에서 볼 수 있듯이 사이드카 컨테이너의 로깅 에이전트를 애플리케이션(같은 포드에 위치)에 추가하고 두 컨테이너 간에 emptyDir 볼륨을 공유합니다. 그런 다음 로그를 공유 볼륨에 쓰도록 애플리케이션을 구성하고 필요할 때 로그를 읽고 전달하도록 로깅 에이전트를 구성합니다.

이 패턴에서는 네이티브 Docker 및 Kubernetes 로깅 메커니즘을 사용하지 않으므로 로그 로테이션을 처리해야 합니다. 로깅 에이전트가 로그 로테이션을 처리하지 않는 경우 동일한 포드의 다른 사이드카 컨테이너가 로테이션을 처리할 수 있습니다.

로그 관리를 위한 사이드카 패턴
그림 2. 로그 관리를 위한 사이드카 패턴

컨테이너의 스테이트리스(Stateless) 및 불변성 확인

중요도: 높음

컨테이너를 처음 사용하는 경우 일반적인 서버처럼 취급하지 마세요. 예를 들어 실행 중인 컨테이너 내에서 애플리케이션 업데이트하거나 취약점이 발생하는 경우 실행 중인 컨테이너를 패치하려는 생각이 들 수 있습니다.

컨테이너는 근본적으로 이 방식으로 작동하도록 설계되지 않았습니다. 컨테이너는 스테이트리스(Stateless)불변성으로 설계됩니다.

스테이트리스(Stateless)

스테이트리스(Stateless)는 모든 상태(모든 종류의 영구 데이터)가 컨테이너 외부에 저장됨을 의미합니다. 이 외부 저장소는 사용자의 필요에 따라 여러 형식을 취할 수 있습니다.

  • 파일을 저장하려면 Cloud Storage와 같은 객체 저장소를 사용하는 것이 좋습니다.
  • 사용자 세션과 같은 정보를 저장하려면 Redis 또는 Memcached와 같은 외부 저지연 키-값 저장소를 사용하는 것이 좋습니다.
  • 블록 레벨 저장소가 필요한 경우(예를 들어 데이터베이스용) 컨테이너에 연결된 외부 디스크를 사용할 수 있습니다. GKE의 경우 영구 디스크를 사용하는 것이 좋습니다.

이러한 옵션을 사용함으로써 컨테이너 자체에서 데이터를 제거하게 되므로 언제든 데이터 손실 걱정 없이 깔끔하게 컨테이너를 종료 및 삭제할 수 있습니다. 이전 컨테이너를 대체하기 위해 새 컨테이너가 생성되는 경우 새 컨테이너를 같은 데이터스토어에 연결하거나 같은 디스크에 결합하기만 하면 됩니다.

불변성

불변성은 컨테이너가 수명 동안 수정되지 않음을 의미합니다. 즉, 업데이트, 패치, 구성 변경이 없습니다. 애플리케이션 코드를 업데이트하거나 패치를 적용해야 하는 경우 새 이미지를 빌드하고 다시 배포합니다. 불변성을 통해 배포의 안전성과 반복 가능성이 높아집니다. 롤백해야 하는 경우 이전 이미지를 다시 배포하기만 하면 됩니다. 이 방식을 통해 모든 환경에 동일한 컨테이너 이미지를 배포하여 최대한 동일하게 만들 수 있습니다.

다양한 환경에서 동일한 컨테이너 이미지를 사용하려면 컨테이너 구성(수신 포트, 런타임 옵션 등)을 외부화하는 것이 좋습니다. 컨테이너는 일반적으로 환경 변수 또는 특정 경로에 마운트된 구성 파일을 사용하여 구성됩니다. Kubernetes에서는 SecretsConfigMaps를 사용하여 구성을 환경 변수 또는 파일로 컨테이너에 삽입할 수 있습니다.

구성을 업데이트해야 하는 경우 업데이트된 구성으로 새 컨테이너를 배포합니다(동일한 이미지 기반).

포드의 구성 파일로 마운트된 ConfigMap을 사용하여 배포의 구성을 업데이트하는 방법 예시
그림 3. 포드의 구성 파일로 마운트된 ConfigMap을 사용하여 배포의 구성을 업데이트하는 방법 예시

스테이트리스(Stateless) 및 불변성의 조합은 컨테이너 기반 인프라의 차별화 요소 중 하나입니다. 이 조합을 통해 배포를 자동화하고 빈도 및 안정성을 높일 수 있습니다.

높은 권한을 가진 컨테이너 방지

중요도: 높음

가상 머신 또는 베어 메탈 서버에서 루트 사용자로 애플리케이션을 실행하지 말아야 합니다. 이유는 간단합니다. 애플리케이션이 침해되는 경우 공격자가 서버 전체 액세스 권한을 가질 수 있기 때문입니다. 같은 이유로 높은 권한을 가진 컨테이너 사용도 피해야 합니다. 높은 권한을 가진 컨테이너는 컨테이너의 거의 모든 보안 기능을 우회해서 호스트 머신의 모든 디바이스에 액세스할 수 있는 컨테이너입니다.

높은 권한을 가진 컨테이너를 사용해야 한다고 판단되는 경우 다음 대안을 고려하세요.

  • Kubernetes의 securityContext 옵션 또는 Docker의 --cap-add 플래그를 통해 컨테이너에 특정 기능을 부여합니다. Docker 문서에는 기본적으로 사용 설정되는 기능과 명시적으로 사용 설정해야 하는 기능이 모두 나와 있습니다.
  • 애플리케이션이 실행을 위해 호스트 설정을 수정해야 하는 경우 사이드카 컨테이너 또는 init 컨테이너에서 설정을 수정하세요. 애플리케이션과 달리 이러한 컨테이너는 내부 또는 외부 트래픽에 노출될 필요가 없으므로 더욱 격리됩니다.
  • Kubernetes의 sysctls를 수정해야 하는 경우 전용 주석을 사용하세요.

정책 컨트롤러를 사용하여 Kubernetes에서 권한이 있는 컨테이너를 금지할 수 있습니다. Kubernetes 클러스터에서는 정책 컨트롤러를 사용하여 구성된 정책을 위반하는 포드를 만들 수 없습니다.

애플리케이션을 손쉽게 모니터링 가능하도록 설정

중요도: 높음

로깅과 마찬가지로 모니터링은 애플리케이션 관리의 필수적인 부분입니다. 컨테이너화된 애플리케이션 모니터링은 많은 측면에서 컨테이너화되지 않은 애플리케이션 모니터링에 적용되는 것과 동일한 원칙에 따릅니다. 그러나 컨테이너화된 인프라는 일반적으로 매우 동적이고 컨테이너가 자주 생성 또는 삭제되므로 매번 모니터링 시스템을 다시 구성하기는 어렵습니다.

모니터링은 크게 블랙박스 모니터링화이트박스 모니터링, 두 가지로 분류됩니다. 블랙박스 모니터링은 최종 사용자처럼 외부에서 애플리케이션을 살펴보는 것을 나타냅니다. 블랙박스 모니터링은 제공하고자 하는 최종 서비스가 사용 가능하고 작동하는 경우 유용합니다. 블랙박스 모니터링은 인프라 외부에서 작동하므로 기존 인프라와 컨테이너화된 인프라를 구분하지 않습니다.

화이트박스 모니터링은 일종의 높은 권한 액세스를 사용하여 애플리케이션을 살펴보고 최종 사용자가 볼 수 없는 애플리케이션 행동에 대한 측정항목을 수집하는 것을 나타냅니다. 화이트박스 모니터링은 인프라의 가장 깊은 레이어를 살펴봐야 하므로 기존 인프라와 컨테이너화된 인프라의 차이가 큽니다.

Kubernetes 커뮤니티에서 널리 사용되는 화이트박스 모니터링 옵션은 모니터링해야 하는 포드를 자동으로 검색할 수 있는 시스템인 Prometheus입니다. Prometheus는 pod에서 측정항목을 스크레이핑하며, 특정 형식을 예상합니다. Google Cloud는 대규모로 Prometheus를 수동으로 관리하고 운영할 필요 없이 워크로드를 전역적으로 모니터링하고 알림을 제공할 수 있는 서비스인 Google Cloud Managed Service for Prometheus를 제공합니다. 기본적으로 Google Cloud Managed Service for Prometheus는 GKE 클러스터에서 시스템 측정항목을 수집하여 Cloud Monitoring으로 전송하도록 구성되어 있습니다. 자세한 내용은 GKE의 관측 가능성을 참조하세요.

Prometheus 또는 Monitoring을 활용하려면 애플리케이션에서 측정항목을 노출해야 합니다. 다음 두 방법에서 어떻게 하는지 보여줍니다.

측정항목 HTTP 엔드포인트

측정항목 HTTP 엔드포인트의 작동 방식은 후술할 애플리케이션 상태 노출의 엔드포인트와 비슷합니다. 애플리케이션의 내부 측정항목을 일반적으로 /metrics URI에 노출합니다. 응답은 다음과 같습니다.

http_requests_total{method="post",code="200"} 1027
http_requests_total{method="post",code="400"}    3
http_requests_total{method="get",code="200"} 10892
http_requests_total{method="get",code="400"}    97

이 예시에서 http_requests_total은 측정항목이고 methodcode는 라벨이며 맨 오른쪽 숫자는 라벨에 대한 이 측정항목의 값입니다. 여기서 애플리케이션은 시작부터 HTTP GET 요청에 400 오류 코드로 97번 응답했습니다.

많은 언어에 존재하는 Prometheus 클라이언트 라이브러리를 통해 이 HTTP 엔드포인트를 쉽게 생성할 수 있습니다. OpenCensus도 이 형식을 사용하여 측정항목을 내보낼 수 있습니다(그 외에도 많은 기능이 있음). 공개 인터넷에 이 엔드포인트를 노출하지 마세요.

공식 Prometheus 문서에는 이 주제에 대해 더 세부적인 설명이 나와 있습니다. 또한 사이트 안정성 엔지니어링6장에서 화이트박스 및 블랙박스 모니터링에 대해 자세히 알아볼 수 있습니다.

모니터링을 위한 사이드카 패턴

/metrics HTTP 엔드포인트로 계측할 수 없는 애플리케이션도 있습니다. 표준화된 모니터링을 유지하려면 사이드카 패턴을 사용하여 올바른 형식으로 측정항목을 내보내는 것이 좋습니다.

로그 애그리게이터 사이드카 패턴 섹션에서 사이드카 컨테이너를 사용하여 애플리케이션 로그를 관리하는 방법을 설명했습니다. 모니터링에도 같은 패턴을 사용할 수 있습니다. 사이드카 컨테이너는 애플리케이션에 의해 노출되는 측정항목을 전역 모니터링 시스템이 이해할 수 있는 형식 및 프로토콜로 변환하는 모니터링 에이전트를 호스팅합니다.

구체적인 예를 들면 자바 애플리케이션과 자바 관리 확장(JMX)이 있습니다. 많은 자바 애플리케이션이 JMX를 사용하여 측정항목을 노출합니다. Prometheus 형식으로 측정항목을 노출하도록 애플리케이션을 다시 작성할 필요 없이 jmx_exporter를 사용할 수 있습니다. jmx_exporter는 JMX를 통해 애플리케이션에서 측정항목을 수집하고 Prometheus가 읽을 수 있는 /metrics 엔드포인트를 통해 노출합니다. 이 방식에는 애플리케이션 설정을 수정하는 데 사용될 수 있는 JMX 엔드포인트의 노출이 제한된다는 이점도 있습니다.

모니터링을 위한 사이드카 패턴
그림 4. 모니터링을 위한 사이드카 패턴

애플리케이션 상태 노출

중요도: 보통

애플리케이션은 프로덕션에서의 용이한 관리를 위해 애플리케이션이 실행 중인지, 정상 상태인지, 트래픽을 받을 준비가 되어 있는지, 어떻게 작동하고 있는지 등에 대한 상태를 전체 시스템에 알려야 합니다.

Kubernetes에는 활성 여부 조사와 준비 여부 조사, 두 가지 유형의 상태 확인이 있습니다. 각 유형마다 용도가 있으며 이 섹션에서는 이 용도에 대해 설명합니다. 여러 가지 방법으로 두 가지 모두 구현할 수 있지만(컨테이너 내에서 명령어 실행 또는 TCP 포트 확인 등) 선호되는 방법은 권장사항에 설명된 HTTP 엔드포인트를 사용하는 방법입니다. 이 주제에 대한 자세한 내용은 Kubernetes 문서를 참조하세요.

활성 여부 프로브

활성 여부 프로브를 구현하기 위한 권장되는 방법은 애플리케이션에서 /healthz HTTP 엔드포인트를 노출하는 것입니다. 이 엔드포인트에 요청이 수신되면 애플리케이션은 정상 상태로 간주되는 경우 '200 OK' 응답을 보내야 합니다. Kubernetes에서 정상 상태는 컨테이너를 삭제하거나 다시 시작할 필요가 없음을 의미합니다. 정상 상태를 구성하는 요소는 애플리케이션마다 다르지만 일반적으로 다음을 의미합니다.

  • 애플리케이션이 실행 중입니다.
  • 기본 종속 항목이 충족됩니다(예를 들어 데이터베이스에 액세스할 수 있음).

준비 여부 프로브

준비 여부 프로브를 구현하기 위한 권장되는 방법은 애플리케이션에서 /ready HTTP 엔드포인트를 노출하는 것입니다. 애플리케이션은 이 엔드포인트에서 요청을 수신하면 트래픽을 수신할 준비가 된 경우 '200 OK' 응답을 보내야 합니다. 트래픽을 받을 준비가 됐다는 것은 다음을 의미합니다.

  • 애플리케이션이 정상입니다.
  • 모든 잠재적 초기화 단계가 완료되었습니다.
  • 애플리케이션으로 전송된 유효한 요청의 결과가 오류가 아닙니다.

Kubernetes는 준비 여부 조사를 사용하여 애플리케이션 배포를 조정합니다. 배포를 업데이트하면 Kubernetes는 해당 배포에 속한 포드의 롤링 업데이트를 수행합니다. 기본 업데이트 정책은 한 번에 하나의 포드를 업데이트하는 것입니다. Kubernetes는 다음 포드를 업데이트하기 전에 새 포드가 준비될 때까지 기다립니다(준비 여부 프로브 결과에 따름).

루트로 실행하지 않기

중요도: 보통

컨테이너는 격리를 제공합니다. 기본 설정에서 Docker 컨테이너 내의 프로세스는 호스트 머신 또는 다른 코로케이션 컨테이너의 정보에 액세스할 수 없습니다. 그러나 컨테이너는 호스트 머신의 커널을 공유하므로 이 블로그 게시물에 설명된 대로 격리는 가상 머신만큼 완전하지는 않습니다. 공격자는 컨테이너 밖으로 나갈 수 있게 해주는, 아직 알려지지 않은 취약점(Docker 또는 Linux 커널 자체에서)을 발견할 수 있습니다. 공격자가 취약점을 발견하고 프로세스가 컨테이너 내에서 루트로 실행 중인 경우 공격자는 호스트 머신에 대한 루트 액세스 권한을 얻게 됩니다.

왼쪽: 가상 머신이 가상화된 하드웨어를 사용
오른쪽: 컨테이너의 애플리케이션이 호스트 커널을 사용
그림 5. 왼쪽에서는 가상 머신이 가상화된 하드웨어를 사용합니다. 오른쪽에서는 컨테이너의 애플리케이션이 호스트 커널을 사용합니다.

이 가능성을 피하기 위한 권장사항은 컨테이너 내에서 루트로 프로세스를 실행하지 않는 것입니다. 정책 컨트롤러를 사용하여 Kubernetes에서 이 동작을 강제할 수 있습니다. Kubernetes에서 pod를 만들 때 runAsUser 옵션을 사용하여 프로세스를 실행 중인 Linux 사용자를 지정합니다. 이 방식은 Dockerfile의 USER 안내를 재정의합니다.

현실에서는 어려운 점이 있습니다. 유명 소프트웨어 패키지의 상당수는 기본 프로세스를 루트로 실행합니다. 루트 실행을 방지하려면 권한이 높지 않은 알 수 없는 사용자로 실행 가능하도록 컨테이너를 설계합니다. 이 방식은 많은 경우 다양한 폴더의 권한을 조정해야 함을 의미합니다. 컨테이너에서 컨테이너별 단일 애플리케이션 권장사항을 따르고 있고 루트가 아닌 단일 사용자로 단일 애플리케이션을 실행하는 경우 쓰기 작업이 필요한 폴더 및 파일에 대한 쓰기 권한을 모든 사용자에게 부여하는 한편 다른 모든 폴더와 파일은 루트 권한으로만 쓸 수 있게 설정할 수 있습니다.

컨테이너가 권장사항을 준수하는지 여부를 확인하는 간단한 방법은 임의의 사용자로 로컬에서 컨테이너를 실행하여 올바르게 작동하는지 여부를 테스트하는 것입니다. [YOUR_CONTAINER]를 컨테이너 이름으로 바꿉니다.

docker run --user $((RANDOM+1)) [YOUR_CONTAINER]

컨테이너에 외부 볼륨이 필요한 경우 fsGroup Kubernetes 옵션을 구성하여 특정 Linux 그룹에 이 볼륨의 소유권을 부여할 수 있습니다. 이 구성은 외부 파일 소유권 문제를 해결합니다.

권한이 높지 않은 사용자가 실행하는 프로세스는 1024 미만의 포트를 바인딩할 수 없습니다. 이 경우 다른 포트로 트래픽을 라우팅하도록 Kubernetes Services를 구성할 수 있으므로 일반적으로는 문제가 되지 않습니다. 예를 들어 포트 8080에 바인딩하고 Kubernetes Service로 포트 80의 트래픽을 리디렉션하도록 HTTP 서버를 구성할 수 있습니다.

이미지 버전은 신중하게 선택

중요도: 보통

Dockerfile의 기본 이미지든 Kubernetes에 배포되는 이미지든 Docker 이미지를 사용하는 경우 사용 중인 이미지의 태그를 선택해야 합니다.

대부분의 공개 및 비공개 이미지는 컨테이너 빌드를 위한 권장사항에 설명된 것과 비슷한 태그 시스템을 따릅니다. 이미지가 체계적인 버전 관리와 비슷한 시스템을 사용하는 경우 태그에 대한 몇 가지 구체적인 사항을 고려해야 합니다.

가장 중요한 점은 'latest' 태그는 이미지에서 다른 이미지로 자주 이동할 수 있다는 것입니다. 따라서 이 태그에 의존하여 예측 가능하거나 재현 가능한 빌드를 확보할 수는 없습니다. 예를 들어 다음 Dockerfile을 보겠습니다.

FROM debian:latest
RUN apt-get -y update && \ apt-get -y install nginx

이 Dockerfile에서 서로 다른 시점에 두 번 이미지를 빌드하는 경우 두 가지 버전의 Debian 및 NGINX가 만들어질 수 있습니다. 그 대신 다음과 같은 수정된 버전을 고려하세요.

FROM debian:11.6
RUN apt-get -y update && \ apt-get -y install nginx

더 정밀한 태그를 사용함으로써 결과 이미지가 항상 Debian의 특정 세부 버전을 기반으로 하도록 할 수 있습니다. 특정 Debian 버전은 특정 NGINX 버전으로 이어지므로 빌드 중인 이미지를 훨씬 더 효과적으로 제어할 수 있습니다.

이는 빌드 뿐만 아니라 실행 시에도 해당됩니다. Kubernetes 매니페스트의 'latest' 태그를 참조하는 경우 Kubernetes가 사용할 버전을 보장할 수 없습니다. 클러스터의 다양한 노드가 서로 다른 시점의 'latest' 태그를 가져올 수 있습니다. 가져오기 사이에 태그가 업데이트된 경우 여러 노드가 여러 이미지(모두 어느 시점에 'latest' 태그가 지정됨)를 실행하는 상황이 발생할 수 있습니다.

이상적으로는 FROM 라인에 항상 immutable 태그를 사용해야 합니다. 이 태그를 통해 재현 가능한 빌드를 확보할 수 있습니다. 그러나 보안 측면에서 몇 가지 타협점이 있습니다. 사용할 버전을 더 많이 고정할수록 이미지의 보안 패치 자동화 수준은 낮아집니다. 사용 중인 이미지에서 적절한 체계적인 버전 관리를 사용 중인 경우 패치 버전(즉, 'X.Y.Z'의 'Z')에 이전 버전과 호환되지 않는 변경사항이 있을 수 없습니다. 'X.Y' 태그를 사용하면 자동으로 버그 수정을 받을 수 있습니다.

예를 들어 이름이 'SuperSoft'인 소프트웨어가 있다고 가정해보겠습니다. SuperSoft의 보안 프로세스는 새로운 패치 버전을 통해 취약점을 수정하는 것입니다. 사용자는 SuperSoft를 맞춤설정하고자 하며 다음 Dockerfile을 작성했습니다.

FROM supersoft:1.2.3
RUN a-command

얼마 후 공급업체가 취약점을 발견하고 문제를 해결하기 위해 SuperSoft 버전 1.2.4를 출시합니다. 이 경우 SuperSoft의 패치에 대한 최신 정보를 확인하고 그에 따라 Dockerfile을 업데이트하는 것은 사용자의 책임입니다. 대신 Dockerfile에 FROM supersoft:1.2를 사용하는 경우 새 버전을 자동으로 가져옵니다.

결국 사용 중인 각 외부 이미지의 태그 시스템을 면밀하게 살펴보고 이러한 이미지를 빌드하는 사람들의 신뢰도를 파악하고 사용할 태그를 결정해야 합니다.

다음 단계

Google Cloud에 대한 참조 아키텍처, 다이어그램, 권장사항을 살펴봅니다. Cloud 아키텍처 센터 살펴보기